// OpenSTA, Static Timing Analyzer // Copyright (c) 2025, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. // // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // This notice may not be removed or altered from any source distribution. #include "Sdc.hh" #include #include "Stats.hh" #include "Debug.hh" #include "Mutex.hh" #include "Report.hh" #include "Variables.hh" #include "PatternMatch.hh" #include "MinMax.hh" #include "TimingRole.hh" #include "TimingArc.hh" #include "Liberty.hh" #include "Transition.hh" #include "PortDirection.hh" #include "Network.hh" #include "RiseFallMinMax.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 "HpinDrvrLoad.hh" #include "search/Levelize.hh" #include "Corner.hh" #include "Graph.hh" namespace sta { using std::swap; bool 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); return (first1 < first2) || (first1 == first2 && second1 < second2); } //////////////////////////////////////////////////////////////// typedef Vector ClockPairSeq; typedef Set PvtSet; static ExceptionThruSeq * clone(ExceptionThruSeq *thrus, Network *network); //////////////////////////////////////////////////////////////// Sdc::Sdc(StaState *sta) : StaState(sta), derating_factors_(nullptr), clk_index_(0), clock_pin_map_(PinIdHash(network_)), clock_leaf_pin_map_(PinIdHash(network_)), clk_hpin_disables_(network_), propagated_clk_pins_(network_), clk_latencies_(network_), clk_insertions_(network_), clk_sense_map_(network_), clk_gating_check_(nullptr), cycle_acctings_(this), input_delay_pin_map_(PinIdLess(network_)), input_delay_ref_pin_map_(PinIdLess(network_)), input_delay_leaf_pin_map_(PinIdLess(network_)), input_delay_internal_pin_map_(PinIdLess(network_)), input_delay_index_(0), output_delay_pin_map_(PinIdLess(network_)), output_delay_ref_pin_map_(PinIdLess(network_)), output_delay_leaf_pin_map_(PinIdLess(network_)), disabled_pins_(network_), disabled_ports_(network_), disabled_wire_edges_(network_), disabled_clk_gating_checks_inst_(network_), disabled_clk_gating_checks_pin_(network_), exception_id_(0), have_thru_hpin_exceptions_(false), first_thru_edge_exceptions_(0, PinPairHash(network_), PinPairEqual()), path_delay_internal_from_(network_), path_delay_internal_from_break_(network_), path_delay_internal_to_(network_), path_delay_internal_to_break_(network_) { sdc_ = this; initVariables(); if (corners_) makeCornersAfter(corners_); setWireload(nullptr, MinMaxAll::all()); setWireloadSelection(nullptr, MinMaxAll::all()); setOperatingConditions(nullptr, MinMaxAll::all()); makeDefaultArrivalClock(); } 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_++, network_); default_arrival_clk_->initClk(0, false, 0.0, waveform, nullptr, network_); } 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_leaf_pin_map_.clear(); clk_latencies_.clear(); edge_clk_latency_.clear(); 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_delays_.clear(); input_delay_pin_map_.clear(); input_delay_index_ = 0; input_delay_ref_pin_map_.clear(); input_delay_leaf_pin_map_.clear(); input_delay_internal_pin_map_.clear(); output_delays_.clear(); output_delay_pin_map_.clear(); output_delay_leaf_pin_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(); setWireload(nullptr, MinMaxAll::all()); setWireloadSelection(nullptr, MinMaxAll::all()); // Operating conditions are owned by Liberty libraries. setOperatingConditions(nullptr, MinMaxAll::all()); clk_index_ = 0; makeDefaultArrivalClock(); unsetTimingDerate(); } void Sdc::initVariables() { analysis_type_ = AnalysisType::ocv; wireload_mode_ = WireloadMode::unknown; max_area_ = 0.0; path_delays_without_to_ = false; clk_hpin_disables_valid_ = false; have_clk_slew_limits_ = false; } void Sdc::deleteConstraints() { clocks_.deleteContents(); delete default_arrival_clk_; clock_pin_map_.deleteContents(); clock_leaf_pin_map_.deleteContents(); clk_latencies_.deleteContents(); clk_insertions_.deleteContents(); clk_groups_name_map_.deleteContents(); clearClkGroupExclusions(); pin_clk_uncertainty_map_.deleteContents(); inter_clk_uncertainties_.deleteContents(); delete clk_gating_check_; clk_gating_check_ = nullptr; 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(); for (auto [pin, checks] : data_checks_from_map_) { checks->deleteContents(); delete checks; } for (auto [pin, checks] : data_checks_to_map_) delete checks; input_delays_.deleteContents(); input_delay_pin_map_.deleteContents(); input_delay_leaf_pin_map_.deleteContents(); input_delay_ref_pin_map_.deleteContents(); input_delay_internal_pin_map_.deleteContents(); output_delays_.deleteContents(); output_delay_pin_map_.deleteContents(); output_delay_ref_pin_map_.deleteContents(); output_delay_leaf_pin_map_.deleteContents(); clk_hpin_disables_.deleteContentsClear(); clk_hpin_disables_valid_ = false; clearCycleAcctings(); deleteExceptions(); clearGroupPathMap(); deleteDeratingFactors(); removeNetLoadCaps(); clk_sense_map_.clear(); for (int mm_index : MinMax::rangeIndex()) instance_pvt_maps_[mm_index].deleteContentsClear(); } void Sdc::removeNetLoadCaps() { if (!net_wire_cap_maps_.empty()) { for (int corner_index = 0; corner_index < corners_->count(); corner_index++) { net_wire_cap_maps_[corner_index].clear(); drvr_pin_wire_cap_maps_[corner_index].clear(); port_ext_cap_maps_[corner_index].deleteContentsClear(); } } } void Sdc::removeLibertyAnnotations() { for (auto [cell, disable] : disabled_cell_ports_) { if (disable->all()) cell->setIsDisabledConstraint(false); if (disable->from()) { for (LibertyPort *from : *disable->from()) from->setIsDisabledConstraint(false); } if (disable->to()) { for (LibertyPort *to : *disable->to()) to->setIsDisabledConstraint(false); } if (disable->timingArcSets()) { for (TimingArcSet *arc_set : *disable->timingArcSets()) arc_set->setIsDisabledConstraint(false); } if (disable->fromTo()) { for (const LibertyPortPair &pair : *disable->fromTo()) { const LibertyPort *from = pair.first; const LibertyPort *to = pair.second; for (TimingArcSet *arc_set : cell->timingArcSets(from, to)) arc_set->setIsDisabledConstraint(false); } } } for (LibertyPort *port : disabled_lib_ports_) port->setIsDisabledConstraint(false); } void Sdc::deleteNetBefore(const Net *net) { for (int corner_index = 0; corner_index < corners_->count(); corner_index++) { net_wire_cap_maps_[corner_index].erase(net); for (const Pin *pin : *network_->drivers(net)) drvr_pin_wire_cap_maps_[corner_index].erase(pin); } } // see Sdc::isConstrained void Sdc::deleteInstanceBefore(const Instance *inst) { instance_pvt_maps_[MinMax::minIndex()].erase(inst); instance_pvt_maps_[MinMax::maxIndex()].erase(inst); inst_derating_factors_.erase(inst); inst_clk_gating_check_map_.erase(inst); disabled_inst_ports_.erase(inst); inst_latch_borrow_limit_map_.erase(inst); inst_min_pulse_width_map_.erase(inst); for (ExceptionPath *exception : exceptions_) exception->deleteInstance(inst, network_); first_from_inst_exceptions_.erase(inst); first_thru_inst_exceptions_.erase(inst); first_to_inst_exceptions_.erase(inst); } void Sdc::makeCornersBefore() { removeNetLoadCaps(); } void Sdc::makeCornersAfter(Corners *corners) { corners_ = corners; port_ext_cap_maps_.resize(corners_->count(), PortExtCapMap(PortIdLess(network_))); net_wire_cap_maps_.resize(corners_->count(), NetWireCapMap(NetIdLess(network_))); drvr_pin_wire_cap_maps_.resize(corners_->count(), PinWireCapMap(PinIdLess(network_))); } //////////////////////////////////////////////////////////////// bool Sdc::isConstrained(const Pin *pin) const { Port *port = network_->isTopLevelPort(pin) ? network_->port(pin) : nullptr; return clock_pin_map_.hasKey(pin) || propagated_clk_pins_.hasKey(pin) || 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_pin_map_.hasKey(pin) || output_delay_pin_map_.hasKey(pin) || pin_cap_limit_map_.hasKey(pin) || disabled_pins_.hasKey(pin) || disabled_ports_.hasKey(port) || disabled_clk_gating_checks_pin_.hasKey(pin) || first_from_pin_exceptions_.hasKey(pin) || first_thru_pin_exceptions_.hasKey(pin) || 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) || (port && (port_slew_limit_map_.hasKey(port) || port_cap_limit_map_.hasKey(port) || port_fanout_limit_map_.hasKey(port) || hasPortExtCap(port))); } bool Sdc::isConstrained(const Instance *inst) const { return instance_pvt_maps_[MinMax::minIndex()].hasKey(inst) || instance_pvt_maps_[MinMax::maxIndex()].hasKey(inst) || inst_derating_factors_.hasKey(inst) || inst_clk_gating_check_map_.hasKey(inst) || disabled_inst_ports_.hasKey(inst) || first_from_inst_exceptions_.hasKey(inst) || first_thru_inst_exceptions_.hasKey(inst) || first_to_inst_exceptions_.hasKey(inst) || inst_latch_borrow_limit_map_.hasKey(inst) || inst_min_pulse_width_map_.hasKey(inst); } bool Sdc::isConstrained(const Net *net) const { return net_derating_factors_.hasKey(net) || hasNetWireCap(net) || net_res_map_.hasKey(net) || first_thru_net_exceptions_.hasKey(net); } //////////////////////////////////////////////////////////////// PortSeq Sdc::allInputs(bool no_clks) { PortSeq ports; Instance *top_inst = network_->topInstance(); InstancePinIterator *pin_iter = network_->pinIterator(top_inst); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); const Port *port = network_->port(pin); PortDirection *dir = network_->direction(port); if (dir->isAnyInput() && !(no_clks && isClock(pin))) portMembers(port, ports); } delete pin_iter; return ports; } PortSeq Sdc::allOutputs() { PortSeq ports; Instance *top_inst = network_->topInstance(); InstancePinIterator *pin_iter = network_->pinIterator(top_inst); while (pin_iter->hasNext()) { const Pin *pin = pin_iter->next(); const Port *port = network_->port(pin); PortDirection *dir = network_->direction(port); if (dir->isAnyOutput()) portMembers(port, ports); } delete pin_iter; return ports; } void Sdc::portMembers(const Port *port, PortSeq &ports) { if (network_->isBus(port)) { PortMemberIterator *member_iter = network_->memberIterator(port); while (member_iter->hasNext()) { Port *member = member_iter->next(); ports.push_back(member); } delete member_iter; } else ports.push_back(port); } void Sdc::setAnalysisType(AnalysisType analysis_type) { analysis_type_ = analysis_type; } void Sdc::setOperatingConditions(OperatingConditions *op_cond, const MinMaxAll *min_max) { for (auto mm_index : min_max->rangeIndex()) 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) const { int mm_index = min_max->index(); return operating_conditions_[mm_index]; } const Pvt * Sdc::pvt(const Instance *inst, const MinMax *min_max) const { const InstancePvtMap &pvt_map = instance_pvt_maps_[min_max->index()]; return pvt_map.findKey(inst); } void Sdc::setPvt(const Instance *inst, const MinMaxAll *min_max, const Pvt &pvt) { for (auto mm_index : min_max->rangeIndex()) { InstancePvtMap &pvt_map = instance_pvt_maps_[mm_index]; pvt_map[inst] = new Pvt(pvt); } } void Sdc::voltage(const MinMax *min_max, // Return values. float &voltage, bool &exists) { voltages_.value(min_max, voltage, exists); } void Sdc::voltage(const Net *net, const MinMax *min_max, // Return values. float &voltage, bool &exists) { exists = false; if (net_voltage_map_.hasKey(net)) net_voltage_map_[net].value(min_max, voltage, exists); } void Sdc::setVoltage(const MinMax *min_max, float voltage) { voltages_.setValue(min_max, voltage); } void Sdc::setVoltage(const Net *net, const MinMax *min_max, float voltage) { net_voltage_map_[net].setValue(min_max, voltage); } //////////////////////////////////////////////////////////////// void Sdc::setTimingDerate(TimingDerateType type, PathClkOrData clk_data, const RiseFallBoth *rf, const EarlyLate *early_late, float derate) { if (derating_factors_ == nullptr) derating_factors_ = new DeratingFactorsGlobal; derating_factors_->setFactor(type, clk_data, rf, early_late, derate); } void Sdc::setTimingDerate(const Net *net, PathClkOrData clk_data, const RiseFallBoth *rf, const EarlyLate *early_late, float derate) { DeratingFactorsNet *factors = net_derating_factors_.findKey(net); if (factors == nullptr) { factors = new DeratingFactorsNet; net_derating_factors_[net] = factors; } factors->setFactor(clk_data, rf, early_late, derate); } void Sdc::setTimingDerate(const Instance *inst, TimingDerateCellType type, PathClkOrData clk_data, const RiseFallBoth *rf, const EarlyLate *early_late, float derate) { DeratingFactorsCell *factors = inst_derating_factors_.findKey(inst); if (factors == nullptr) { factors = new DeratingFactorsCell; inst_derating_factors_[inst] = factors; } factors->setFactor(type, clk_data, rf, early_late, derate); } void Sdc::setTimingDerate(const LibertyCell *cell, TimingDerateCellType type, PathClkOrData clk_data, const RiseFallBoth *rf, const EarlyLate *early_late, float derate) { DeratingFactorsCell *factors = cell_derating_factors_.findKey(cell); if (factors == nullptr) { factors = new DeratingFactorsCell; cell_derating_factors_[cell] = factors; } factors->setFactor(type, clk_data, rf, early_late, derate); } float Sdc::timingDerateInstance(const Pin *pin, TimingDerateCellType type, PathClkOrData clk_data, const RiseFall *rf, const EarlyLate *early_late) const { const Instance *inst = network_->instance(pin); DeratingFactorsCell *factors = inst_derating_factors_.findKey(inst); if (factors) { float factor; bool exists; factors->factor(type, clk_data, rf, early_late, factor, exists); if (exists) return factor; } 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, rf, early_late, factor, exists); if (exists) return factor; } } if (derating_factors_) { float factor; bool exists; derating_factors_->factor(type, clk_data, rf, early_late, factor, exists); if (exists) return factor; } return 1.0; } float Sdc::timingDerateNet(const Pin *pin, PathClkOrData clk_data, const RiseFall *rf, const EarlyLate *early_late) const { const Net *net = network_->net(pin); DeratingFactorsNet *factors = net_derating_factors_.findKey(net); if (factors) { float factor; bool exists; factors->factor(clk_data, rf, early_late, factor, exists); if (exists) return factor; } if (derating_factors_) { float factor; bool exists; derating_factors_->factor(TimingDerateType::net_delay, clk_data, rf, early_late, factor, exists); if (exists) return factor; } return 1.0; } void Sdc::unsetTimingDerate() { deleteDeratingFactors(); } void Sdc::swapDeratingFactors(Sdc *sdc1, Sdc *sdc2) { swap(sdc1->derating_factors_, sdc2->derating_factors_); swap(sdc1->net_derating_factors_, sdc2->net_derating_factors_); swap(sdc1->inst_derating_factors_, sdc2->inst_derating_factors_); swap(sdc1->cell_derating_factors_, sdc2->cell_derating_factors_); } void Sdc::deleteDeratingFactors() { net_derating_factors_.deleteContents(); inst_derating_factors_.deleteContents(); cell_derating_factors_.deleteContents(); delete derating_factors_; derating_factors_ = nullptr; } //////////////////////////////////////////////////////////////// void Sdc::setDriveCell(const LibertyLibrary *library, const LibertyCell *cell, const Port *port, const LibertyPort *from_port, float *from_slews, const LibertyPort *to_port, const RiseFallBoth *rf, const MinMaxAll *min_max) { ensureInputDrive(port)->setDriveCell(library, cell, from_port, from_slews, to_port, rf, min_max); } void Sdc::setInputSlew(const Port *port, const RiseFallBoth *rf, const MinMaxAll *min_max, float slew) { ensureInputDrive(port)->setSlew(rf, min_max, slew); } void Sdc::setDriveResistance(const Port *port, const RiseFallBoth *rf, const MinMaxAll *min_max, float res) { ensureInputDrive(port)->setDriveResistance(rf, min_max, res); } InputDrive * Sdc::ensureInputDrive(const Port *port) { InputDrive *drive = input_drive_map_.findKey(port); if (drive == nullptr) { drive = new InputDrive; input_drive_map_[port] = drive; } return drive; } //////////////////////////////////////////////////////////////// void Sdc::setSlewLimit(Clock *clk, const RiseFallBoth *rf, const PathClkOrData clk_data, const MinMax *min_max, float slew) { clk->setSlewLimit(rf, 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 RiseFall *rf, const PathClkOrData clk_data, const MinMax *min_max, float &slew, bool &exists) { clk->slewLimit(rf, clk_data, min_max, slew, exists); } void Sdc::slewLimit(Port *port, const MinMax *min_max, float &slew, bool &exists) { slew = INF; 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(Cell *cell, const MinMax *min_max, float &slew, bool &exists) { slew = INF; 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 = min_max->initValue(); 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_++, network_); clk->setIsPropagated(variables_->propagateAllClocks()); clocks_.push_back(clk); // Use the copied name in the map. clock_name_map_[clk->name()] = clk; } clk->initClk(pins, add_to_pins, period, waveform, comment, network_); 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, 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_++, network_); clocks_.push_back(clk); clock_name_map_[clk->name()] = clk; } clk->initGeneratedClk(pins, add_to_pins, src_pin, master_clk, divide_by, multiply_by, duty_cycle, invert, combinational, edges, edge_shifts, variables_->propagateAllClocks(), comment, network_); makeClkPinMappings(clk); clearCycleAcctings(); invalidateGeneratedClks(); clkHpinDisablesInvalid(); return clk; } void Sdc::invalidateGeneratedClks() const { for (auto clk : clocks_) { 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; if (pins) { for (const Pin *pin : *pins) { ClockSet *pin_clks = clock_pin_map_.findKey(pin); if (pin_clks) { for (Clock *clk : *pin_clks) clks.insert(clk); } } } for (Clock *clk : clks) { deleteClkPinMappings(clk); for (const Pin *pin : *pins) clk->deletePin(pin); if (clk != defining_clk) { if (clk->pins().empty()) removeClock(clk); else { clk->makeLeafPins(network_); // One of the remaining clock pins may use a vertex pin that // was deleted above. makeClkPinMappings(clk); } } } } void Sdc::deleteClkPinMappings(Clock *clk) { for (const Pin *pin : clk->pins()) { ClockSet *pin_clks = clock_pin_map_.findKey(pin); if (pin_clks) { pin_clks->erase(clk); if (pin_clks->empty()) { clock_pin_map_.erase(pin); delete pin_clks; } } } for (const Pin *pin : clk->leafPins()) { ClockSet *pin_clks = clock_leaf_pin_map_.findKey(pin); if (pin_clks) { pin_clks->erase(clk); if (pin_clks->empty()) { clock_leaf_pin_map_.erase(pin); delete pin_clks; } } } } void Sdc::makeClkPinMappings(Clock *clk) { for (const Pin *pin : clk->pins()) { ClockSet *pin_clks = clock_pin_map_.findKey(pin); if (pin_clks == nullptr) { pin_clks = new ClockSet; clock_pin_map_.insert(pin, pin_clks); } pin_clks->insert(clk); } for (const Pin *pin : clk->leafPins()) { ClockSet *pin_clks = clock_leaf_pin_map_.findKey(pin); if (pin_clks == nullptr) { pin_clks = new ClockSet; clock_leaf_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); clock_name_map_.erase(clk->name()); delete clk; } // Delete references to clk as a master clock. void Sdc::deleteMasterClkRefs(Clock *clk) { for (auto gclk : clocks_) { if (gclk->isGenerated() && gclk->masterClk() == clk) { gclk->setMasterClk(nullptr); } } } 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::isLeafPinClock(const Pin *pin) const { ClockSet *clks = findLeafPinClocks(pin); return clks && !clks->empty(); } bool Sdc::isLeafPinNonGeneratedClock(const Pin *pin) const { ClockSet *clks = findLeafPinClocks(pin); if (clks) { for (Clock *clk : *clks) { if (!clk->isGenerated()) return true; } return false; } else return false; } ClockSet * Sdc::findLeafPinClocks(const Pin *pin) const { return clock_leaf_pin_map_.findKey(pin); } ClockSet * Sdc::findClocks(const Pin *pin) const { return clock_pin_map_.findKey(pin); } ClockSeq Sdc::findClocksMatching(PatternMatch *pattern) const { ClockSeq matches; if (!pattern->hasWildcards()) { Clock *clk = findClock(pattern->pattern()); if (clk) matches.push_back(clk); } else { for (auto clk : clocks_) { if (pattern->match(clk->name())) matches.push_back(clk); } } return matches; } void Sdc::sortedClocks(ClockSeq &clks) { for (auto clk : clocks_) clks.push_back(clk); sort(clks, ClkNameLess()); } ClockEdge * Sdc::defaultArrivalClockEdge() const { return default_arrival_clk_->edge(RiseFall::rise()); } //////////////////////////////////////////////////////////////// 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) { } ClkHpinDisableLess::ClkHpinDisableLess(const Network *network) : network_(network) { } bool ClkHpinDisableLess::operator()(const ClkHpinDisable *disable1, const ClkHpinDisable *disable2) const { int clk_index1 = disable1->clk()->index(); int clk_index2 = disable2->clk()->index(); const Pin *from_pin1 = disable1->fromPin(); const Pin *from_pin2 = disable2->fromPin(); const Pin *to_pin1 = disable1->toPin(); const Pin *to_pin2 = disable2->toPin(); return clk_index1 < clk_index2 || (clk_index1 == clk_index2 && (network_->id(to_pin1) < network_->id(to_pin2) || (from_pin1 == from_pin2 && network_->id(from_pin1) < network_->id(from_pin2)))); } class FindClkHpinDisables : public HpinDrvrLoadVisitor { public: FindClkHpinDisables(Clock *clk, const Network *network, Sdc *sdc); bool drvrLoadExists(const Pin *drvr, const Pin *load); protected: virtual void visit(HpinDrvrLoad *drvr_load); void makeClkHpinDisables(const Pin *clk_src, const Pin *drvr, const Pin *load); Clock *clk_; PinPairSet drvr_loads_; const Network *network_; Sdc *sdc_; }; FindClkHpinDisables::FindClkHpinDisables(Clock *clk, const Network *network, Sdc *sdc) : HpinDrvrLoadVisitor(), clk_(clk), drvr_loads_(network), network_(network), sdc_(sdc) { } void FindClkHpinDisables::visit(HpinDrvrLoad *drvr_load) { const Pin *drvr = drvr_load->drvr(); const Pin *load = drvr_load->load(); makeClkHpinDisables(drvr, drvr, load); PinSet *hpins_from_drvr = drvr_load->hpinsFromDrvr(); for (const Pin *hpin : *hpins_from_drvr) makeClkHpinDisables(hpin, drvr, load); drvr_loads_.insert(PinPair(drvr, load)); } void FindClkHpinDisables::makeClkHpinDisables(const Pin *clk_src, const Pin *drvr, const Pin *load) { ClockSet *clks = sdc_->findClocks(clk_src); if (clks) { for (Clock *clk : *clks) { 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(const Pin *drvr, const Pin *load) { PinPair probe(drvr, load); return drvr_loads_.hasKey(probe); } void Sdc::ensureClkHpinDisables() { if (!clk_hpin_disables_valid_) { clk_hpin_disables_.deleteContentsClear(); for (auto clk : clocks_) { for (const Pin *src : clk->pins()) { if (network_->isHierarchical(src)) { FindClkHpinDisables visitor1(clk, network_, this); visitHpinDrvrLoads(src, network_, &visitor1); PinSeq loads, drvrs; PinSet visited_drvrs(network_); FindNetDrvrLoads visitor2(nullptr, visited_drvrs, loads, drvrs, network_); network_->visitConnectedPins(src, visitor2); // Disable fanouts from the src driver pins that do // not go thru the hierarchical src pin. for (const Pin *drvr : drvrs) { for (const Pin *load : loads) { if (!visitor1.drvrLoadExists(drvr, load)) makeClkHpinDisable(clk, drvr, load); } } } } } clk_hpin_disables_valid_ = true; } } void Sdc::makeClkHpinDisable(const Clock *clk, const Pin *drvr, const 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::clkHpinDisablesInvalid() { clk_hpin_disables_valid_ = false; for (auto clk : clocks_) clk->makeLeafPins(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->leafPins().hasKey(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); removeClockLatency(clk, nullptr); } void Sdc::removePropagatedClock(Clock *clk) { clk->setIsPropagated(false); } void Sdc::setPropagatedClock(Pin *pin) { propagated_clk_pins_.insert(pin); removeClockLatency(nullptr, pin); } void Sdc::removePropagatedClock(Pin *pin) { propagated_clk_pins_.erase(pin); } bool Sdc::isPropagatedClock(const Pin *pin) { return propagated_clk_pins_.hasKey(pin); } void Sdc::setClockSlew(Clock *clk, const RiseFallBoth *rf, const MinMaxAll *min_max, float slew) { clk->setSlew(rf, min_max, slew); } void Sdc::removeClockSlew(Clock *clk) { clk->removeSlew(); } void Sdc::setClockLatency(Clock *clk, Pin *pin, const RiseFallBoth *rf, const MinMaxAll *min_max, float delay) { ClockLatency probe(clk, pin); ClockLatency *latency = clk_latencies_.findKey(&probe); if (latency == nullptr) { latency = new ClockLatency(clk, pin); clk_latencies_.insert(latency); } latency->setDelay(rf, min_max, delay); // set_clock_latency removes set_propagated_clock on the same object. if (clk && pin == nullptr) 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) { clk_latencies_.erase(latency); delete latency; } void Sdc::deleteClockLatenciesReferencing(Clock *clk) { for (auto iter = clk_latencies_.cbegin(); iter != clk_latencies_.cend(); ) { ClockLatency *latency = *iter; if (latency->clock() == clk) { iter = clk_latencies_.erase(iter); delete latency; } else iter++; } } bool Sdc::hasClockLatency(const Pin *pin) const { ClockLatency probe(nullptr, pin); return clk_latencies_.hasKey(&probe); } void Sdc::clockLatency(const Clock *clk, const Pin *pin, const RiseFall *rf, 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(rf, min_max, latency, exists); } if (!exists) { ClockLatency probe(nullptr, pin); ClockLatency *latencies = clk_latencies_.findKey(&probe); if (latencies) latencies->delay(rf, min_max, latency, exists); } } void Sdc::clockLatency(const Clock *clk, const RiseFall *rf, const MinMax *min_max, // Return values. float &latency, bool &exists) const { latency = 0.0; exists = false; ClockLatency probe(clk, nullptr); ClockLatency *latencies = clk_latencies_.findKey(&probe); if (latencies) latencies->delay(rf, min_max, latency, exists); } float Sdc::clockLatency(const Clock *clk, const RiseFall *rf, const MinMax *min_max) const { float latency; bool exists; clockLatency(clk, rf, 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); if (uncertainties == nullptr) { 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; pin_clk_uncertainty_map_.erase(pin); } } } 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 RiseFall *src_rf, const Clock *tgt_clk, const RiseFall *tgt_rf, 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_rf, tgt_rf, setup_hold, uncertainty, exists); else { uncertainty = 0.0; exists = false; } } void Sdc::setClockUncertainty(Clock *from_clk, const RiseFallBoth *from_rf, Clock *to_clk, const RiseFallBoth *to_rf, const SetupHoldAll *setup_hold, float uncertainty) { InterClockUncertainty probe(from_clk, to_clk); InterClockUncertainty *uncertainties = inter_clk_uncertainties_.findKey(&probe); if (uncertainties == nullptr) { uncertainties = new InterClockUncertainty(from_clk, to_clk); inter_clk_uncertainties_.insert(uncertainties); } uncertainties->setUncertainty(from_rf, to_rf, setup_hold, uncertainty); } void Sdc::removeClockUncertainty(Clock *from_clk, const RiseFallBoth *from_rf, Clock *to_clk, const RiseFallBoth *to_rf, const SetupHoldAll *setup_hold) { InterClockUncertainty probe(from_clk, to_clk); InterClockUncertainty *uncertainties = inter_clk_uncertainties_.findKey(&probe); if (uncertainties) { uncertainties->removeUncertainty(from_rf, to_rf, setup_hold); if (uncertainties->empty()) { inter_clk_uncertainties_.erase(uncertainties); delete uncertainties; } } } void Sdc::deleteInterClockUncertainty(InterClockUncertainty *uncertainties) { inter_clk_uncertainties_.erase(uncertainties); delete uncertainties; } void Sdc::deleteInterClockUncertaintiesReferencing(Clock *clk) { for (auto iter = inter_clk_uncertainties_.cbegin(); iter != inter_clk_uncertainties_.cend(); ) { InterClockUncertainty *uncertainties = *iter; if (uncertainties->src() == clk || uncertainties->target() == clk) { iter = inter_clk_uncertainties_.erase(iter); delete uncertainties; } else iter++; } } //////////////////////////////////////////////////////////////// void Sdc::setClockInsertion(const Clock *clk, const Pin *pin, const RiseFallBoth *rf, const MinMaxAll *min_max, const EarlyLateAll *early_late, float delay) { ClockInsertion probe(clk, pin); ClockInsertion *insertion = clk_insertions_.findKey(&probe); if (insertion == nullptr) { insertion = new ClockInsertion(clk, pin); clk_insertions_.insert(insertion); } insertion->setDelay(rf, min_max, early_late, delay); } void Sdc::setClockInsertion(const Clock *clk, const Pin *pin, const RiseFall *rf, const MinMax *min_max, const EarlyLate *early_late, float delay) { ClockInsertion probe(clk, pin); ClockInsertion *insertion = clk_insertions_.findKey(&probe); if (insertion == nullptr) { insertion = new ClockInsertion(clk, pin); clk_insertions_.insert(insertion); } insertion->setDelay(rf, min_max, early_late, delay); } void Sdc::removeClockInsertion(const Clock *clk, const Pin *pin) { ClockInsertion probe(clk, pin); ClockInsertion *insertion = clk_insertions_.findKey(&probe); if (insertion != nullptr) deleteClockInsertion(insertion); } void Sdc::swapClockInsertions(Sdc *sdc1, Sdc *sdc2) { swap(sdc1->clk_insertions_, sdc2->clk_insertions_); } void Sdc::deleteClockInsertion(ClockInsertion *insertion) { clk_insertions_.erase(insertion); delete insertion; } void Sdc::deleteClockInsertionsReferencing(Clock *clk) { for (auto iter = clk_insertions_.cbegin(); iter != clk_insertions_.cend(); ) { ClockInsertion *insertion = *iter; if (insertion->clock() == clk) { iter = clk_insertions_.erase(iter); delete insertion; } else iter++; } } float Sdc::clockInsertion(const Clock *clk, const RiseFall *rf, const MinMax *min_max, const EarlyLate *early_late) const { float insertion; bool exists; clockInsertion(clk, nullptr, rf, min_max, early_late, insertion, exists); return insertion; } bool Sdc::hasClockInsertion(const Pin *pin) const { ClockInsertion probe(nullptr, pin); return clk_insertions_.hasKey(&probe); } void Sdc::clockInsertion(const Clock *clk, const Pin *pin, const RiseFall *rf, const MinMax *min_max, const EarlyLate *early_late, // Return values. float &insertion, bool &exists) const { ClockInsertion *insert = nullptr; if (clk && pin) { ClockInsertion probe(clk, pin); insert = clk_insertions_.findKey(&probe); } if (insert == nullptr && pin) { ClockInsertion probe(nullptr, pin); insert = clk_insertions_.findKey(&probe); } if (insert == nullptr && clk) { ClockInsertion probe(clk, nullptr); insert = clk_insertions_.findKey(&probe); } if (insert) insert->delay(rf, min_max, early_late, insertion, exists); else { insertion = 0.0; exists = false; } } //////////////////////////////////////////////////////////////// ClockLatencyLess::ClockLatencyLess(const Network *network) : network_(network) { } bool ClockLatencyLess::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 == nullptr && clk2) || ((clk1 && clk2 && clk1->index() < clk2->index()) || (clk1 == clk2 && ((pin1 == nullptr && pin2) || (pin1 && pin2 && network_->id(pin1) < network_->id(pin2))))); } //////////////////////////////////////////////////////////////// ClockInsertionkLess::ClockInsertionkLess(const Network *network) : network_(network) { } bool ClockInsertionkLess::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 == nullptr && clk2) || ((clk1 && clk2 && clk1->index() < clk2->index()) || (clk1 == clk2 && ((pin1 == nullptr && pin2) || (pin1 && pin2 && network_->id(pin1) < network_->id(pin2))))); } //////////////////////////////////////////////////////////////// ClockGroups * Sdc::makeClockGroups(const char *name, bool logically_exclusive, bool physically_exclusive, bool asynchronous, bool allow_paths, const char *comment) { char *gen_name = nullptr; if (name == nullptr || name[0] == '\0') 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); clk_groups_name_map_[groups->name()] = groups; stringDelete(gen_name); return groups; } // Generate a name for the clock group. char * Sdc::makeClockGroupsName() { char *name = nullptr; int i = 0; do { i++; stringDelete(name); name = stringPrint("group%d", i); } while (clk_groups_name_map_.hasKey(name)); return name; } void Sdc::makeClockGroup(ClockGroups *clk_groups, ClockSet *clks) { clk_groups->makeClockGroup(clks); } void Sdc::ensureClkGroupExclusions() { if (clk_group_exclusions_.empty()) { for (const auto [name, clk_groups] : clk_groups_name_map_) makeClkGroupExclusions(clk_groups); } } void Sdc::makeClkGroupExclusions(ClockGroups *clk_groups) { if (!(clk_groups->asynchronous() && clk_groups->allowPaths())) { ClockGroupSet *groups = clk_groups->groups(); 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(); for (auto clk1 : *group1) { for (Clock *clk2 : clocks_) { if (clk2 != clk1 && !group1->hasKey(clk2)) clk_group_exclusions_.insert(ClockPair(clk1, clk2)); } } makeClkGroupSame(group1); } void Sdc::makeClkGroupExclusions(ClockGroupSet *groups) { for (auto group1 : *groups) { for (auto group2 : *groups) { if (group1 != group2) { for (auto clk1 : *group1) { for (auto clk2 : *group2) { // ClockPair is symmetric so only add one clk1/clk2 pair. if (clk1->index() < clk2->index()) { clk_group_exclusions_.insert(ClockPair(clk1, clk2)); } } } } } makeClkGroupSame(group1); } } void Sdc::makeClkGroupSame(ClockGroup *group) { for (auto clk1 : *group) { for (auto clk2 : *group) { if (clk1->index() <= clk2->index()) { ClockPair clk_pair(clk1, clk2); if (!clk_group_same_.hasKey(clk_pair)) clk_group_same_.insert(clk_pair); } } } } void Sdc::clearClkGroupExclusions() { clk_group_exclusions_.clear(); clk_group_same_.clear(); } bool Sdc::sameClockGroup(const Clock *clk1, const Clock *clk2) { if (clk1 && clk2) { ClockPair clk_pair(clk1, clk2); bool excluded = clk_group_exclusions_.hasKey(clk_pair); return !excluded; } else return true; } bool Sdc::sameClockGroupExplicit(const Clock *clk1, const Clock *clk2) { ClockPair clk_pair(clk1, clk2); return clk_group_same_.hasKey(clk_pair); } 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 && groups->logicallyExclusive()) removeClockGroups(groups); } else { for (const auto [name, groups] : clk_groups_name_map_) { if (groups->logicallyExclusive()) removeClockGroups(groups); } } } void Sdc::removeClockGroupsPhysicallyExclusive(const char *name) { if (name) { ClockGroups *groups = clk_groups_name_map_.findKey(name); if (groups && groups->physicallyExclusive()) removeClockGroups(groups); } else { for (const auto [name, groups] : clk_groups_name_map_) { if (groups->physicallyExclusive()) removeClockGroups(groups); } } } void Sdc::removeClockGroupsAsynchronous(const char *name) { if (name) { ClockGroups *groups = clk_groups_name_map_.findKey(name); if (groups && groups->asynchronous()) removeClockGroups(groups); } else { for (const auto [name, groups] : clk_groups_name_map_) { if (groups->asynchronous()) removeClockGroups(groups); } } } void Sdc::removeClockGroups(ClockGroups *groups) { clk_groups_name_map_.erase(groups->name()); 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) { for (const auto [name, groups] : clk_groups_name_map_) groups->removeClock(clk); clearClkGroupExclusions(); } //////////////////////////////////////////////////////////////// void Sdc::setClockSense(PinSet *pins, ClockSet *clks, ClockSense sense) { if (clks && clks->empty()) { delete clks; clks = nullptr; } for (const Pin *pin : *pins) { if (clks) { for (const Clock *clk : *clks) setClockSense(pin, clk, sense); } else setClockSense(pin, nullptr, sense); } delete pins; delete clks; } void Sdc::setClockSense(const Pin *pin, const Clock *clk, ClockSense sense) { PinClockPair probe(pin, clk); if (clk_sense_map_.hasKey(probe)) clk_sense_map_[probe] = sense; else { PinClockPair pin_clk(pin, clk); 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; clk_sense_map_.findKey(pin_clk, sense, exists); if (!exists) { PinClockPair pin_clk1(pin, nullptr); clk_sense_map_.findKey(pin_clk1, sense, exists); } return exists && sense == ClockSense::stop; } bool Sdc::clkStopSense(const Pin *to_pin, const Clock *clk, const RiseFall *from_rf, const RiseFall *to_rf) const { PinClockPair pin_clk(to_pin, clk); ClockSense sense; bool exists; clk_sense_map_.findKey(pin_clk, sense, exists); if (!exists) { PinClockPair pin(to_pin, nullptr); clk_sense_map_.findKey(pin, sense, exists); } return exists && (sense == ClockSense::stop || (sense == ClockSense::positive && from_rf != to_rf) || (sense == ClockSense::negative && from_rf == to_rf)); } bool Sdc::clkStopPropagation(const Clock *clk, const Pin *from_pin, const RiseFall *from_rf, const Pin *to_pin, const RiseFall *to_rf) const { return clkStopPropagation(from_pin, clk) || clkStopSense(to_pin, clk, from_rf, to_rf); } PinClockPairLess::PinClockPairLess(const Network *network) : network_(network) { } bool PinClockPairLess::operator()(const PinClockPair &pin_clk1, const PinClockPair &pin_clk2) const { const Pin *pin1 = pin_clk1.first; const Pin *pin2 = pin_clk2.first; const Clock *clk1 = pin_clk1.second; const Clock *clk2 = pin_clk2.second; return pin1 < pin2 || (pin1 == pin2 && ((clk1 == nullptr && clk2) || (clk1 && clk2 && clk1->index() < clk2->index()))); } //////////////////////////////////////////////////////////////// void Sdc::setClockGatingCheck(const RiseFallBoth *rf, const SetupHold *setup_hold, float margin) { if (clk_gating_check_ == nullptr) clk_gating_check_ = new ClockGatingCheck; clk_gating_check_->margins()->setValue(rf, setup_hold, margin); } void Sdc::setClockGatingCheck(Clock *clk, const RiseFallBoth *rf, const SetupHold *setup_hold, float margin) { ClockGatingCheck *check = clk_gating_check_map_.findKey(clk); if (check == nullptr) { check = new ClockGatingCheck(); clk_gating_check_map_[clk] = check; } check->margins()->setValue(rf, setup_hold, margin); } void Sdc::setClockGatingCheck(Instance *inst, const RiseFallBoth *rf, const SetupHold *setup_hold, float margin, LogicValue active_value) { ClockGatingCheck *check = inst_clk_gating_check_map_.findKey(inst); if (check == nullptr) { check = new ClockGatingCheck(); inst_clk_gating_check_map_[inst] = check; } check->margins()->setValue(rf, setup_hold, margin); check->setActiveValue(active_value); } void Sdc::setClockGatingCheck(const Pin *pin, const RiseFallBoth *rf, const SetupHold *setup_hold, float margin, LogicValue active_value) { ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(pin); if (check == nullptr) { check = new ClockGatingCheck(); pin_clk_gating_check_map_[pin] = check; } check->margins()->setValue(rf, setup_hold, margin); check->setActiveValue(active_value); } void Sdc::clockGatingMarginEnablePin(const Pin *enable_pin, const RiseFall *enable_rf, const SetupHold *setup_hold, bool &exists, float &margin) { ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(enable_pin); if (check) check->margins()->value(enable_rf, setup_hold, margin, exists); else exists = false; } void Sdc::clockGatingMarginInstance(Instance *inst, const RiseFall *enable_rf, const SetupHold *setup_hold, bool &exists, float &margin) { ClockGatingCheck *check = inst_clk_gating_check_map_.findKey(inst); if (check) check->margins()->value(enable_rf, setup_hold, margin, exists); else exists = false; } void Sdc::clockGatingMarginClkPin(const Pin *clk_pin, const RiseFall *enable_rf, const SetupHold *setup_hold, bool &exists, float &margin) { ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(clk_pin); if (check) check->margins()->value(enable_rf, setup_hold, margin, exists); else exists = false; } void Sdc::clockGatingMarginClk(const Clock *clk, const RiseFall *enable_rf, const SetupHold *setup_hold, bool &exists, float &margin) { ClockGatingCheck *check = clk_gating_check_map_.findKey(clk); if (check) check->margins()->value(enable_rf, setup_hold, margin, exists); else exists = false; } void Sdc::clockGatingMargin(const RiseFall *enable_rf, const SetupHold *setup_hold, bool &exists, float &margin) { if (clk_gating_check_) clk_gating_check_->margins()->value(enable_rf, 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(); return LogicValue::unknown; } //////////////////////////////////////////////////////////////// // Determine cycle accounting "on demand". CycleAccting * Sdc::cycleAccting(const ClockEdge *src, const ClockEdge *tgt) { LockGuard lock(cycle_acctings_lock_); return cycle_acctings_.cycleAccting(src, tgt); } void Sdc::reportClkToClkMaxCycleWarnings() { cycle_acctings_.reportClkToClkMaxCycleWarnings(report_); } void Sdc::clearCycleAcctings() { cycle_acctings_.clear(); } //////////////////////////////////////////////////////////////// void Sdc::setDataCheck(Pin *from, const RiseFallBoth *from_rf, Pin *to, const RiseFallBoth *to_rf, Clock *clk, const SetupHoldAll *setup_hold, float margin) { DataCheck *check = nullptr; DataCheckSet *checks = data_checks_from_map_.findKey(from); if (checks == nullptr) { checks = new DataCheckSet(DataCheckLess(network_)); data_checks_from_map_[from] = checks; } else { DataCheck probe(from, to, clk); check = checks->findKey(&probe); } if (check == nullptr) check = new DataCheck(from, to, clk); check->setMargin(from_rf, to_rf, setup_hold, margin); checks->insert(check); checks = data_checks_to_map_.findKey(to); if (checks == nullptr) { checks = new DataCheckSet(DataCheckLess(network_)); data_checks_to_map_[to] = checks; } checks->insert(check); } void Sdc::removeDataCheck(Pin *from, const RiseFallBoth *from_rf, Pin *to, const RiseFallBoth *to_rf, Clock *clk, const SetupHoldAll *setup_hold) { 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_rf, to_rf, setup_hold); if (check->empty()) { checks->erase(check); checks = data_checks_to_map_.findKey(to); if (checks) checks->erase(check); 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(const Pin *pin, float limit) { pin_latch_borrow_limit_map_[pin] = limit; } void Sdc::setLatchBorrowLimit(const Instance *inst, float limit) { inst_latch_borrow_limit_map_[inst] = limit; } void Sdc::setLatchBorrowLimit(const Clock *clk, float limit) { clk_latch_borrow_limit_map_[clk] = limit; } void Sdc::deleteLatchBorrowLimitsReferencing(Clock *clk) { clk_latch_borrow_limit_map_.erase(clk); } void Sdc::latchBorrowLimit(const Pin *data_pin, const Pin *enable_pin, const 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 RiseFallBoth *rf, float min_width) { for (auto rf1 : rf->range()) min_pulse_width_.setValue(rf1, min_width); } void Sdc::setMinPulseWidth(const Pin *pin, const RiseFallBoth *rf, float min_width) { RiseFallValues *widths = pin_min_pulse_width_map_.findKey(pin); if (widths == nullptr) { widths = new RiseFallValues; pin_min_pulse_width_map_[pin] = widths; } for (auto rf1 : rf->range()) widths->setValue(rf1, min_width); } void Sdc::setMinPulseWidth(const Instance *inst, const RiseFallBoth *rf, float min_width) { RiseFallValues *widths = inst_min_pulse_width_map_.findKey(inst); if (widths == nullptr) { widths = new RiseFallValues; inst_min_pulse_width_map_[inst] = widths; } for (auto rf1 : rf->range()) widths->setValue(rf1, min_width); } void Sdc::setMinPulseWidth(const Clock *clk, const RiseFallBoth *rf, float min_width) { RiseFallValues *widths = clk_min_pulse_width_map_.findKey(clk); if (widths == nullptr) { widths = new RiseFallValues; clk_min_pulse_width_map_[clk] = widths; } for (auto rf1 : rf->range()) widths->setValue(rf1, min_width); } void Sdc::minPulseWidth(const Pin *pin, const Clock *clk, const RiseFall *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); } if (widths == nullptr) 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; clk_min_pulse_width_map_.erase(clk); } } //////////////////////////////////////////////////////////////// InputDrive * Sdc::findInputDrive(Port *port) { return input_drive_map_.findKey(port); } void Sdc::setInputDelay(const Pin *pin, const RiseFallBoth *rf, const Clock *clk, const RiseFall *clk_rf, const Pin *ref_pin, bool source_latency_included, bool network_latency_included, const MinMaxAll *min_max, bool add, float delay) { ClockEdge *clk_edge = clk ? clk->edge(clk_rf) : nullptr; InputDelay *input_delay = findInputDelay(pin, clk_edge); if (input_delay == nullptr) input_delay = makeInputDelay(pin, clk_edge); if (add) { RiseFallMinMax *delays = input_delay->delays(); delays->mergeValue(rf, min_max, delay); } else { deleteInputDelays(pin, input_delay); RiseFallMinMax *delays = input_delay->delays(); delays->setValue(rf, min_max, delay); } if (ref_pin) { InputDelaySet *ref_inputs = input_delay_ref_pin_map_.findKey(ref_pin); if (ref_inputs == nullptr) { ref_inputs = new InputDelaySet; input_delay_ref_pin_map_[ref_pin] = ref_inputs; } ref_inputs->insert(input_delay); } input_delay->setRefPin(ref_pin); input_delay->setSourceLatencyIncluded(source_latency_included); input_delay->setNetworkLatencyIncluded(network_latency_included); } InputDelay * Sdc::makeInputDelay(const Pin *pin, const ClockEdge *clk_edge) { InputDelay *input_delay = new InputDelay(pin, clk_edge, input_delay_index_++, network_); input_delays_.insert(input_delay); InputDelaySet *inputs = input_delay_pin_map_.findKey(pin); if (inputs == nullptr) { inputs = new InputDelaySet; input_delay_pin_map_[pin] = inputs; } inputs->insert(input_delay); for (const Pin *lpin : input_delay->leafPins()) { InputDelaySet *leaf_inputs = input_delay_leaf_pin_map_[lpin]; if (leaf_inputs == nullptr) { leaf_inputs = new InputDelaySet; input_delay_leaf_pin_map_[lpin] = leaf_inputs; } leaf_inputs->insert(input_delay); if (!network_->isTopLevelPort(lpin)) { InputDelaySet *internal_inputs = input_delay_internal_pin_map_[lpin]; if (internal_inputs == nullptr) { internal_inputs = new InputDelaySet; input_delay_internal_pin_map_[pin] = internal_inputs; } internal_inputs->insert(input_delay); } } return input_delay; } InputDelay * Sdc::findInputDelay(const Pin *pin, const ClockEdge *clk_edge) { InputDelaySet *inputs = input_delay_pin_map_.findKey(pin); if (inputs) { for (InputDelay *input_delay : *inputs) { if (input_delay->clkEdge() == clk_edge) return input_delay; } } return nullptr; } void Sdc::removeInputDelay(const Pin *pin, const RiseFallBoth *rf, const Clock *clk, const RiseFall *clk_rf, const MinMaxAll *min_max) { ClockEdge *clk_edge = clk ? clk->edge(clk_rf) : nullptr; InputDelay *input_delay = findInputDelay(pin, clk_edge); if (input_delay) { RiseFallMinMax *delays = input_delay->delays(); delays->removeValue(rf, min_max); if (delays->empty()) deleteInputDelay(input_delay); } } void Sdc::deleteInputDelays(const Pin *pin, InputDelay *except) { InputDelaySet *input_delays = input_delay_pin_map_[pin]; InputDelaySet::Iterator iter(input_delays); while (iter.hasNext()) { InputDelay *input_delay = iter.next(); if (input_delay != except) deleteInputDelay(input_delay); } } InputDelaySet * Sdc::refPinInputDelays(const Pin *ref_pin) const { return input_delay_ref_pin_map_.findKey(ref_pin); } InputDelaySet * Sdc::inputDelaysLeafPin(const Pin *leaf_pin) { return input_delay_leaf_pin_map_.findKey(leaf_pin); } bool Sdc::hasInputDelay(const Pin *leaf_pin) const { InputDelaySet *input_delays = input_delay_leaf_pin_map_.findKey(leaf_pin); return input_delays && !input_delays->empty(); } bool Sdc::isInputDelayInternal(const Pin *pin) const { return input_delay_internal_pin_map_.hasKey(pin); } void Sdc::deleteInputDelaysReferencing(const Clock *clk) { InputDelaySet::Iterator iter(input_delays_); while (iter.hasNext()) { InputDelay *input_delay = iter.next(); if (input_delay->clock() == clk) deleteInputDelay(input_delay); } } void Sdc::deleteInputDelay(InputDelay *input_delay) { input_delays_.erase(input_delay); const Pin *pin = input_delay->pin(); InputDelaySet *inputs = input_delay_pin_map_[pin]; inputs->erase(input_delay); for (const Pin *lpin : input_delay->leafPins()) { InputDelaySet *inputs = input_delay_leaf_pin_map_[lpin]; inputs->erase(input_delay); } delete input_delay; } void Sdc::swapPortDelays(Sdc *sdc1, Sdc *sdc2) { swap(sdc1->input_delays_, sdc2->input_delays_); swap(sdc1->input_delay_pin_map_, sdc2->input_delay_pin_map_); swap(sdc1->input_delay_ref_pin_map_, sdc2->input_delay_ref_pin_map_); swap(sdc1->input_delay_leaf_pin_map_, sdc2->input_delay_leaf_pin_map_); swap(sdc1->input_delay_internal_pin_map_, sdc2->input_delay_internal_pin_map_); swap(sdc1->input_delay_index_, sdc2->input_delay_index_); swap(sdc1->output_delays_, sdc2->output_delays_); swap(sdc1->output_delay_pin_map_, sdc2->output_delay_pin_map_); swap(sdc1->output_delay_ref_pin_map_, sdc2->output_delay_ref_pin_map_); swap(sdc1->output_delay_leaf_pin_map_, sdc2->output_delay_leaf_pin_map_); } //////////////////////////////////////////////////////////////// void Sdc::setOutputDelay(const Pin *pin, const RiseFallBoth *rf, const Clock *clk, const RiseFall *clk_rf, const Pin *ref_pin, bool source_latency_included, bool network_latency_included, const MinMaxAll *min_max, bool add, float delay) { ClockEdge *clk_edge = clk ? clk->edge(clk_rf) : nullptr; OutputDelay *output_delay = findOutputDelay(pin, clk_edge); if (output_delay == nullptr) output_delay = makeOutputDelay(pin, clk_edge); if (add) { RiseFallMinMax *delays = output_delay->delays(); delays->mergeValue(rf, min_max, delay); } else { deleteOutputDelays(pin, output_delay); RiseFallMinMax *delays = output_delay->delays(); delays->setValue(rf, min_max, delay); } if (ref_pin) { OutputDelaySet *ref_outputs = output_delay_ref_pin_map_.findKey(ref_pin); if (ref_outputs == nullptr) { ref_outputs = new OutputDelaySet; output_delay_ref_pin_map_[ref_pin] = ref_outputs; } ref_outputs->insert(output_delay); } output_delay->setRefPin(ref_pin); output_delay->setSourceLatencyIncluded(source_latency_included); output_delay->setNetworkLatencyIncluded(network_latency_included); } OutputDelay * Sdc::findOutputDelay(const Pin *pin, const ClockEdge *clk_edge) { OutputDelaySet *outputs = output_delay_pin_map_.findKey(pin); if (outputs) { for (OutputDelay *output_delay : *outputs) { if (output_delay->clkEdge() == clk_edge) return output_delay; } } return nullptr; } OutputDelay * Sdc::makeOutputDelay(const Pin *pin, const ClockEdge *clk_edge) { OutputDelay *output_delay = new OutputDelay(pin, clk_edge, network_); output_delays_.insert(output_delay); OutputDelaySet *outputs = output_delay_pin_map_.findKey(pin); if (outputs == nullptr) { outputs = new OutputDelaySet; output_delay_pin_map_[pin] = outputs; } outputs->insert(output_delay); for (const Pin *lpin : output_delay->leafPins()) { OutputDelaySet *leaf_outputs = output_delay_leaf_pin_map_[lpin]; if (leaf_outputs == nullptr) { leaf_outputs = new OutputDelaySet; output_delay_leaf_pin_map_[lpin] = leaf_outputs; } leaf_outputs->insert(output_delay); } return output_delay; } void Sdc::removeOutputDelay(const Pin *pin, const RiseFallBoth *rf, const Clock *clk, const RiseFall *clk_rf, const MinMaxAll *min_max) { ClockEdge *clk_edge = clk ? clk->edge(clk_rf) : nullptr; OutputDelay *output_delay = findOutputDelay(pin, clk_edge); if (output_delay) { RiseFallMinMax *delays = output_delay->delays(); delays->removeValue(rf, min_max); } } void Sdc::deleteOutputDelays(const Pin *pin, OutputDelay *except) { OutputDelaySet *output_delays = output_delay_pin_map_[pin]; OutputDelaySet::Iterator iter(output_delays); while (iter.hasNext()) { OutputDelay *output_delay = iter.next(); if (output_delay != except) deleteOutputDelay(output_delay); } } OutputDelaySet * Sdc::outputDelaysLeafPin(const Pin *leaf_pin) { return output_delay_leaf_pin_map_.findKey(leaf_pin); } bool Sdc::hasOutputDelay(const Pin *leaf_pin) const { return output_delay_leaf_pin_map_.hasKey(leaf_pin); } void Sdc::deleteOutputDelaysReferencing(const Clock *clk) { OutputDelaySet::Iterator iter(output_delays_); while (iter.hasNext()) { OutputDelay *output_delay = iter.next(); if (output_delay->clock() == clk) deleteOutputDelay(output_delay); } } void Sdc::deleteOutputDelay(OutputDelay *output_delay) { output_delays_.erase(output_delay); const Pin *pin = output_delay->pin(); OutputDelaySet *outputs = output_delay_pin_map_[pin]; outputs->erase(output_delay); for (const Pin *lpin : output_delay->leafPins()) { OutputDelaySet *outputs = output_delay_leaf_pin_map_[lpin]; outputs->erase(output_delay); } delete output_delay; } //////////////////////////////////////////////////////////////// void Sdc::setPortExtPinCap(const Port *port, const RiseFall *rf, const Corner *corner, const MinMax *min_max, float cap) { PortExtCap *port_cap = ensurePortExtPinCap(port, corner); port_cap->setPinCap(cap, rf, min_max); } void Sdc::setPortExtWireCap(const Port *port, bool subtract_pin_cap, const RiseFall *rf, const Corner *corner, const MinMax *min_max, float cap) { PortExtCap *port_cap = ensurePortExtPinCap(port, corner); if (subtract_pin_cap) { Pin *pin = network_->findPin(network_->name(port)); cap -= connectedPinCap(pin, rf, corner, min_max); if (cap < 0.0) cap = 0.0; } port_cap->setWireCap(cap, rf, min_max); } PortExtCap * Sdc::portExtCap(const Port *port, const Corner *corner) const { return port_ext_cap_maps_[corner->index()].findKey(port); } bool Sdc::hasPortExtCap(const Port *port) const { for (int corner_index = 0; corner_index < corners_->count(); corner_index++) { if (port_ext_cap_maps_[corner_index].hasKey(port)) return true; } return false; } void Sdc::portExtCap(const Port *port, const RiseFall *rf, const Corner *corner, 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 { PortExtCap *port_cap = port_ext_cap_maps_[corner->index()].findKey(port); if (port_cap) { port_cap->pinCap(rf, min_max, pin_cap, has_pin_cap); port_cap->wireCap(rf, min_max, wire_cap, has_wire_cap); port_cap->fanout(min_max, fanout, has_fanout); } else { pin_cap = 0.0F; has_pin_cap = false; wire_cap = 0.0F; has_wire_cap = false; fanout = 0.0F; has_fanout = false; } } float Sdc::portExtCap(const Port *port, const RiseFall *rf, const Corner *corner, const MinMax *min_max) const { float pin_cap, wire_cap; int fanout; bool has_pin_cap, has_wire_cap, has_fanout; portExtCap(port, rf, corner, 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, const Corner *corner) { return drvr_pin_wire_cap_maps_[corner->index()].hasKey(pin); } void Sdc::drvrPinWireCap(const Pin *pin, const Corner *corner, const MinMax *min_max, // Return values. float &cap, bool &exists, bool &subtract_pin_cap) const { NetWireCaps *net_caps = drvr_pin_wire_cap_maps_[corner->index()].findKey(pin); if (net_caps) { net_caps->value(min_max, cap, exists); subtract_pin_cap = net_caps->subtractPinCap(min_max); } else { cap = 0.0; exists = false; subtract_pin_cap = false; } } void Sdc::setNetWireCap(const Net *net, bool subtract_pin_cap, const Corner *corner, const MinMax *min_max, float wire_cap) { NetWireCaps &net_caps = net_wire_cap_maps_[corner->index()][net]; net_caps.setValue(min_max, wire_cap); net_caps.setSubtractPinCap(subtract_pin_cap, min_max); for (const Pin *pin : *network_->drivers(net)) drvr_pin_wire_cap_maps_[corner->index()][pin] = &net_caps; } bool Sdc::hasNetWireCap(const Net *net) const { for (int i = 0; i < corners_->count(); i++) { if (net_wire_cap_maps_[i].hasKey(net)) return true; } return false; } //////////////////////////////////////////////////////////////// void Sdc::connectedCap(const Pin *pin, const RiseFall *rf, const Corner *corner, const MinMax *min_max, // Return values. float &pin_cap, float &wire_cap, float &fanout, bool &has_net_load) const { netCaps(pin, rf, corner, min_max, pin_cap, wire_cap, fanout, has_net_load); float net_wire_cap; bool subtract_pin_cap; drvrPinWireCap(pin, corner, min_max, net_wire_cap, has_net_load, subtract_pin_cap); if (subtract_pin_cap) pin_cap = 0.0; if (has_net_load) wire_cap += net_wire_cap; } float Sdc::connectedPinCap(const Pin *pin, const RiseFall *rf, const Corner *corner, const MinMax *min_max) { float pin_cap, wire_cap, fanout; bool has_net_load; connectedCap(pin, rf, corner, min_max, pin_cap, wire_cap, fanout, has_net_load); return pin_cap; } class FindNetCaps : public PinVisitor { public: FindNetCaps(const RiseFall *rf, const Corner *corner, const MinMax *min_max, float &pin_cap, float &wire_cap, float &fanout, bool &has_net_load, const Sdc *sdc); virtual void operator()(const Pin *pin); protected: const RiseFall *rf_; const Corner *corner_; const MinMax *min_max_; float &pin_cap_; float &wire_cap_; float &fanout_; bool &has_net_load_; const Sdc *sdc_; }; FindNetCaps::FindNetCaps(const RiseFall *rf, const Corner *corner, const MinMax *min_max, float &pin_cap, float &wire_cap, float &fanout, bool &has_net_load, const Sdc *sdc) : PinVisitor(), rf_(rf), corner_(corner), min_max_(min_max), pin_cap_(pin_cap), wire_cap_(wire_cap), fanout_(fanout), has_net_load_(has_net_load), sdc_(sdc) { } void FindNetCaps::operator()(const Pin *pin) { sdc_->pinCaps(pin, rf_, corner_, min_max_, pin_cap_, wire_cap_, fanout_); } // Capacitances for all pins connected to drvr_pin's net. void Sdc::netCaps(const Pin *drvr_pin, const RiseFall *rf, const Corner *corner, const MinMax *min_max, // Return values. float &pin_cap, float &wire_cap, float &fanout, bool &has_net_load) const { pin_cap = 0.0; wire_cap = 0.0; fanout = 0.0; has_net_load = false; FindNetCaps visitor(rf, corner, min_max, pin_cap, wire_cap, fanout, has_net_load, this); network_->visitConnectedPins(drvr_pin, visitor); } void Sdc::pinCaps(const Pin *pin, const RiseFall *rf, const Corner *corner, const MinMax *min_max, // Return values. float &pin_cap, float &wire_cap, float &fanout) 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, rf, corner, 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++; } } else { LibertyPort *port = network_->libertyPort(pin); if (port) { Instance *inst = network_->instance(pin); pin_cap += portCapacitance(inst, port, rf, corner, min_max); if (port->direction()->isAnyInput()) fanout++; } } } float Sdc::portCapacitance(Instance *inst, LibertyPort *port, const RiseFall *rf, const Corner *corner, const MinMax *min_max) const { const Pvt *inst_pvt = nullptr; if (inst) inst_pvt = pvt(inst, min_max); LibertyPort *corner_port = port->cornerPort(corner, min_max); OperatingConditions *op_cond = operatingConditions(min_max); return corner_port->capacitance(rf, min_max, op_cond, inst_pvt); } float Sdc::pinCapacitance(const Pin *pin, const RiseFall *rf, const Corner *corner, const MinMax *min_max) { LibertyPort *port = network_->libertyPort(pin); if (port) { Instance *inst = network_->instance(pin); return portCapacitance(inst, port, rf, corner, min_max); } else return 0.0; } //////////////////////////////////////////////////////////////// void Sdc::setResistance(const Net *net, const MinMaxAll *min_max, float res) { MinMaxFloatValues &values = net_res_map_[net]; values.setValue(min_max, res); } void Sdc::resistance(const 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(const Port *port, const Corner *corner, const MinMax *min_max, int fanout) { PortExtCap *port_cap = ensurePortExtPinCap(port, corner); port_cap->setFanout(fanout, min_max); } void Sdc::portExtFanout(const Port *port, const Corner *corner, const MinMax *min_max, // Return values. int &fanout, bool &exists) { PortExtCap *port_cap = portExtCap(port, corner); if (port_cap) port_cap->fanout(min_max, fanout, exists); else { fanout = 0.0; exists = false; } } int Sdc::portExtFanout(Port *port, const Corner *corner, const MinMax *min_max) { int fanout; bool exists; portExtFanout(port, corner, min_max, fanout, exists); if (exists) return fanout; else return 0.0; } PortExtCap * Sdc::ensurePortExtPinCap(const Port *port, const Corner *corner) { PortExtCap *port_cap = port_ext_cap_maps_[corner->index()].findKey(port); if (port_cap == nullptr) { port_cap = new PortExtCap(port); port_ext_cap_maps_[corner->index()][port] = port_cap; } return port_cap; } void Sdc::swapPortExtCaps(Sdc *sdc1, Sdc *sdc2) { for (int corner_index = 0; corner_index < sdc1->corners()->count(); corner_index++) { swap(sdc1->port_ext_cap_maps_[corner_index], sdc2->port_ext_cap_maps_[corner_index]); swap(sdc1->net_wire_cap_maps_[corner_index], sdc2->net_wire_cap_maps_[corner_index]); } } //////////////////////////////////////////////////////////////// void Sdc::disable(LibertyCell *cell, LibertyPort *from, LibertyPort *to) { DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell); if (disabled_cell == nullptr) { disabled_cell = new DisabledCellPorts(cell); disabled_cell_ports_[cell] = disabled_cell; } if (from && to) { disabled_cell->setDisabledFromTo(from, to); for (TimingArcSet *arc_set : cell->timingArcSets(from, to)) 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); for (TimingArcSet *arc_set : cell->timingArcSets(from, to)) 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); if (disabled_cell == nullptr) { 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) { disabled_lib_ports_.erase(port); port->setIsDisabledConstraint(false); } void Sdc::disable(Port *port) { disabled_ports_.insert(port); } void Sdc::removeDisable(Port *port) { disabled_ports_.erase(port); } void Sdc::disable(Instance *inst, LibertyPort *from, LibertyPort *to) { DisabledInstancePorts *disabled_inst = disabled_inst_ports_.findKey(inst); if (disabled_inst == nullptr) { 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(); } void Sdc::removeDisable(Instance *inst, LibertyPort *from, LibertyPort *to) { DisabledInstancePorts *disabled_inst = disabled_inst_ports_.findKey(inst); if (disabled_inst) { 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 pair(from, to); disabled_wire_edges_.insert(pair); } void Sdc::removeDisable(Pin *from, Pin *to) { PinPair probe(from, to); disabled_wire_edges_.erase(probe); } void Sdc::disable(Edge *edge) { disabled_edges_.insert(edge); edge->setIsDisabledConstraint(true); } void Sdc::removeDisable(Edge *edge) { disabled_edges_.erase(edge); 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(const Pin *drvr, const Pin *load); PinPairSet *pairs_; Graph *graph_; }; DisableEdgesThruHierPin::DisableEdgesThruHierPin(PinPairSet *pairs, Graph *graph) : HierPinThruVisitor(), pairs_(pairs), graph_(graph) { } void DisableEdgesThruHierPin::visit(const Pin *drvr, const Pin *load) { PinPair pair(drvr, load); pairs_->insert(pair); } void Sdc::disable(const 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); } class RemoveDisableEdgesThruHierPin : public HierPinThruVisitor { public: RemoveDisableEdgesThruHierPin(PinPairSet *pairs, Graph *graph); protected: virtual void visit(const Pin *drvr, const Pin *load); PinPairSet *pairs_; Graph *graph_; }; RemoveDisableEdgesThruHierPin::RemoveDisableEdgesThruHierPin(PinPairSet *pairs, Graph *graph) : HierPinThruVisitor(), pairs_(pairs), graph_(graph) { } void RemoveDisableEdgesThruHierPin::visit(const Pin *drvr, const Pin *load) { PinPair pair(drvr, load); pairs_->erase(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 disabled_pins_.erase(pin); } bool Sdc::isDisabled(const Pin *pin) const { Port *port = network_->port(pin); LibertyPort *lib_port = network_->libertyPort(pin); return disabled_pins_.hasKey(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(from_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(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; } const DisabledInstancePortsMap * Sdc::disabledInstancePorts() const { 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) { disabled_clk_gating_checks_inst_.erase(inst); } void Sdc::removeDisableClockGatingCheck(Pin *pin) { disabled_clk_gating_checks_pin_.erase(pin); } bool Sdc::isDisableClockGatingCheck(const Instance *inst) { return disabled_clk_gating_checks_inst_.hasKey(inst); } bool Sdc::isDisableClockGatingCheck(const Pin *pin) { return disabled_clk_gating_checks_pin_.hasKey(pin); } //////////////////////////////////////////////////////////////// void Sdc::setLogicValue(const 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(const Pin *pin, LogicValue value) { case_value_map_[pin] = value; } void Sdc::removeCaseAnalysis(const Pin *pin) { case_value_map_.erase(pin); } 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 RiseFallBoth *from_rf) { 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_rf, true, network_); else return nullptr; } bool Sdc::isExceptionStartpoint(const Pin *pin) const { Net *net = network_->net(pin); const LibertyPort *port = network_->libertyPort(pin); return ((network_->isTopLevelPort(pin) && network_->direction(pin)->isAnyInput()) || (port && port->isRegClk()) || (port && port->isLatchData())) // Pins connected to power/ground are invalid. && !(net && (network_->isPower(net) || network_->isGround(net))) && !network_->isHierarchical(pin); } ExceptionThru * Sdc::makeExceptionThru(PinSet *pins, NetSet *nets, InstanceSet *insts, const RiseFallBoth *rf) { if ((pins && !pins->empty()) || (nets && !nets->empty()) || (insts && !insts->empty())) return new ExceptionThru(pins, nets, insts, rf, true, network_); else return nullptr; } ExceptionTo * Sdc::makeExceptionTo(PinSet *pins, ClockSet *clks, InstanceSet *insts, const RiseFallBoth *rf, const RiseFallBoth *end_rf) { if ((pins && !pins->empty()) || (clks && !clks->empty()) || (insts && !insts->empty()) || (rf != RiseFallBoth::riseFall()) || (end_rf != RiseFallBoth::riseFall())) return new ExceptionTo(pins, clks, insts, rf, end_rf, true, network_); else return nullptr; } // Valid endpoints include gated clock enables which are not // known until clock arrivals are determined. bool Sdc::isExceptionEndpoint(const Pin *pin) { Net *net = network_->net(pin); bool has_checks = false; const LibertyPort *port = network_->libertyPort(pin); if (port) { // Look for timing checks to the pin witihout using the graph because // it may not exist. LibertyCell *cell = port->libertyCell(); for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) { if (arc_set->role()->isTimingCheck()) { has_checks = true; break; } } } return ((network_->isTopLevelPort(pin) && network_->direction(pin)->isAnyOutput()) || has_checks || (port && port->isLatchData())) // Pins connected to power/ground are invalid. && !(net && (network_->isPower(net) || network_->isGround(net))) && !network_->isHierarchical(pin); } 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, bool break_path, float delay, const char *comment) { checkFromThrusTo(from, thrus, to); PathDelay *exception = new PathDelay(from, thrus, to, min_max, ignore_clk_latency, break_path, delay, true, comment); addException(exception); } void Sdc::recordPathDelayInternalFrom(ExceptionPath *exception) { ExceptionFrom *from = exception->from(); if (from && from->hasPins()) { for (const Pin *pin : *from->pins()) { if (!isExceptionStartpoint(pin)) { path_delay_internal_from_.insert(pin); if (exception->breakPath()) path_delay_internal_from_break_.insert(pin); } } } } void Sdc::unrecordPathDelayInternalFrom(ExceptionPath *exception) { ExceptionFrom *from = exception->from(); if (from && from->hasPins() && !path_delay_internal_from_.empty()) { for (const Pin *pin : *from->pins()) { if (!isExceptionStartpoint(pin) && !pathDelayFrom(pin)) { path_delay_internal_from_.erase(pin); if (exception->breakPath()) path_delay_internal_from_break_.erase(pin); } } } } bool Sdc::pathDelayFrom(const Pin *pin) { ExceptionPathSet *exceptions = first_from_pin_exceptions_.findKey(pin); for (ExceptionPath *exception : *exceptions) { if (exception->isPathDelay()) return true; } return false; } bool Sdc::isPathDelayInternalFrom(const Pin *pin) const { return path_delay_internal_from_.hasKey(pin); } bool Sdc::isPathDelayInternalFromBreak(const Pin *pin) const { return path_delay_internal_from_break_.hasKey(pin); } const PinSet & Sdc::pathDelayInternalFrom() const { return path_delay_internal_from_; } void Sdc::recordPathDelayInternalTo(ExceptionPath *exception) { ExceptionTo *to = exception->to(); if (to && to->hasPins()) { for (const Pin *pin : *to->pins()) { if (!(hasLibertyCheckTo(pin) || network_->isTopLevelPort(pin))) { path_delay_internal_to_.insert(pin); if (exception->breakPath()) path_delay_internal_to_break_.insert(pin); } } } } void Sdc::unrecordPathDelayInternalTo(ExceptionPath *exception) { ExceptionTo *to = exception->to(); if (to && to->hasPins() && !path_delay_internal_to_.empty()) { for (const Pin *pin : *to->pins()) { if (!(hasLibertyCheckTo(pin) || network_->isTopLevelPort(pin)) && !pathDelayTo(pin)) { path_delay_internal_to_.erase(pin); if (exception->breakPath()) path_delay_internal_to_break_.erase(pin); } } } } bool Sdc::hasLibertyCheckTo(const Pin *pin) { const Instance *inst = network_->instance(pin); LibertyCell *cell = network_->libertyCell(inst); if (cell) { LibertyPort *port = network_->libertyPort(pin); if (port) { for (TimingArcSet *arc_set : cell->timingArcSets(nullptr, port)) { if (arc_set->role()->isTimingCheckBetween()) return true; } } } return false; } bool Sdc::pathDelayTo(const Pin *pin) { ExceptionPathSet *exceptions = first_to_pin_exceptions_.findKey(pin); for (ExceptionPath *exception : *exceptions) { if (exception->isPathDelay()) return true; } return false; } bool Sdc::isPathDelayInternalTo(const Pin *pin) const { return path_delay_internal_to_.hasKey(pin); } bool Sdc::isPathDelayInternalToBreak(const Pin *pin) const { return path_delay_internal_to_break_.hasKey(pin); } //////////////////////////////////////////////////////////////// void Sdc::clearGroupPathMap() { // GroupPath exceptions are deleted with other exceptions. // Delete group_path name strings. for (auto [name, groups] : group_path_map_) { stringDelete(name); groups->deleteContents(); 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) report_->critical(1490, "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); // Clone the group_path because it may get merged and hence deleted // by addException. ExceptionFrom *from1 = group_path->from() ? group_path->from()->clone(network_):nullptr; ExceptionThruSeq *thrus1 = exceptionThrusClone(group_path->thrus(), network_); ExceptionTo *to1 = group_path->to() ? group_path->to()->clone(network_) : nullptr; ExceptionPath *clone = group_path->clone(from1, thrus1, to1, true); addException(clone); // A named group path can have multiple exceptions. GroupPathSet *groups = group_path_map_.findKey(name); if (groups == nullptr) { groups = new GroupPathSet(network_); group_path_map_[stringCopy(name)] = groups; } if (groups->hasKey(group_path)) // Exact copy of existing group path. delete group_path; else 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); } //////////////////////////////////////////////////////////////// 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() { for (GraphLoop *loop : levelize_->loops()) makeLoopExceptions(loop); } // Make a -thru pin false path from every edge entering the loop // around the loop and back. void Sdc::makeLoopExceptions(GraphLoop *loop) { debugPrint(debug_, "loop", 2, "Loop false path"); for (Edge *edge : *loop->edges()) { 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(const Pin *loop_input_pin, const Pin *loop_pin, const 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(const Pin *pin, ExceptionThruSeq *thrus) { debugPrint(debug_, "levelize", 2, " %s", network_->pathName(pin)); PinSet *pins = new PinSet(network_); pins->insert(pin); ExceptionThru *thru = makeExceptionThru(pins, nullptr, nullptr, RiseFallBoth::riseFall()); thrus->push_back(thru); } void Sdc::deleteLoopExceptions() { // erase prevents range iteration. ExceptionPathSet::Iterator except_iter(exceptions_); while (except_iter.hasNext()) { ExceptionPath *except = except_iter.next(); if (except->isLoop()) deleteException(except); } } //////////////////////////////////////////////////////////////// void Sdc::addException(ExceptionPath *exception) { debugPrint(debug_, "exception_merge", 1, "add exception for %s", exception->asString(network_)); if (exception->isPathDelay()) { recordPathDelayInternalFrom(exception); recordPathDelayInternalTo(exception); if (exception->to() == nullptr) 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() // There is only one filter so there are no competing priorities. && !exception->isFilter()) { PinSet *pins1 = from->pins() ? new PinSet(*from->pins()) : nullptr; InstanceSet *insts1 = from->instances() ? new InstanceSet(*from->instances()) : nullptr; ExceptionFrom *from1 = new ExceptionFrom(pins1, nullptr, insts1, from->transition(), true, network_); ExceptionThruSeq *thrus1 = exceptionThrusClone(exception->thrus(), network_); ExceptionTo *to = exception->to(); ExceptionTo *to1 = to ? to->clone(network_) : nullptr; ExceptionPath *exception1 = exception->clone(from1, thrus1, to1, true); debugPrint(debug_, "exception_merge", 1, " split exception for %s", exception1->asString(network_)); addException1(exception1); ClockSet *clks2 = new ClockSet(*from->clks()); ExceptionFrom *from2 = new ExceptionFrom(nullptr, clks2, nullptr, from->transition(), true, network_); ExceptionThruSeq *thrus2 = exceptionThrusClone(exception->thrus(), network_); ExceptionTo *to2 = to ? to->clone(network_) : nullptr; ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true); debugPrint(debug_, "exception_merge", 1, " split exception for %s", 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() ? exception->from()->clone(network_):nullptr; ExceptionThruSeq *thrus1 = exceptionThrusClone(exception->thrus(), network_); 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, to->transition(), to->endTransition(), true, network_); ExceptionPath *exception1 = exception->clone(from1, thrus1, to1, true); debugPrint(debug_, "exception_merge", 1, " split exception for %s", exception1->asString(network_)); addException2(exception1); ExceptionFrom *from2 = exception->from() ? exception->from()->clone(network_):nullptr; ExceptionThruSeq *thrus2 = exceptionThrusClone(exception->thrus(), network_); ClockSet *clks2 = new ClockSet(*to->clks()); ExceptionTo *to2 = new ExceptionTo(nullptr, clks2, nullptr, to->transition(), to->endTransition(), true, network_); ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true); debugPrint(debug_, "exception_merge", 1, " split exception for %s", 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) { debugPrint(debug_, "exception_merge", 1, "find matches for %s", exception->asString(network_)); ExceptionPathSet matches; findMatchingExceptions(exception, matches); ExceptionPathSet expanded_matches; for (ExceptionPath *match : matches) // 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); for (ExceptionPath *match : matches) deleteException(match); for (ExceptionPath *match : expanded_matches) addException(match); } 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(); findMatchingExceptionsPins(exception, from->pins(), first_from_pin_exceptions_, matches); findMatchingExceptionsInsts(exception, from->instances(), first_from_inst_exceptions_, matches); 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_.empty() && thru->nets()) { for (const Net *net : *thru->nets()) { // 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) { for (ExceptionPath *match : *potential_matches) { ExceptionThru *match_thru = (*match->thrus())[0]; if (match_thru->nets()->hasKey(net) && match->overrides(exception) && match->intersectsPts(exception, network_)) 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 (clks) { ExceptionPathSet clks_matches; for (Clock *clk : *clks) 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 (pins) { ExceptionPathSet pins_matches; for (const Pin *pin : *pins) 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 (insts) { ExceptionPathSet insts_matches; for (const Instance *inst : *insts) 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) { for (ExceptionPath *match : *potential_matches) { if (match->overrides(exception) && match->intersectsPts(exception, network_)) 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(network_); from_cpy->deleteObjects(excluding->from(), network_); if (from_cpy->hasObjects()) { ExceptionThruSeq *thrus_cpy = nullptr; if (thrus) thrus_cpy = clone(thrus, network_); ExceptionTo *to_cpy = nullptr; if (to) to_cpy = to->clone(network_); 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, network_); if (thru_cpy->hasObjects()) { ExceptionFrom *from_cpy = nullptr; if (from) from_cpy = from->clone(network_); ExceptionThruSeq *thrus_cpy = new ExceptionThruSeq; for (ExceptionThru *thru1 : *thrus) { if (thru1 == thru) thrus_cpy->push_back(thru_cpy); else { ExceptionThru *thru_cpy = thru->clone(network_); thrus_cpy->push_back(thru_cpy); } } ExceptionTo *to_cpy = nullptr; if (to) to_cpy = to->clone(network_); 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(network_); to_cpy->deleteObjects(excluding->to(), network_); if (to_cpy->hasObjects()) { ExceptionFrom *from_cpy = nullptr; if (from) from_cpy = from->clone(network_); ExceptionThruSeq *thrus_cpy = nullptr; 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; for (ExceptionThru *thru : *thrus) { ExceptionThru *thru_cpy = thru->clone(network); thrus_cpy->push_back(thru_cpy); } return thrus_cpy; } //////////////////////////////////////////////////////////////// void Sdc::recordException(ExceptionPath *exception) { exceptions_.insert(exception); exception->setId(++exception_id_); recordMergeHashes(exception); recordExceptionFirstPts(exception); recordExceptionPins(exception); checkForThruHpins(exception); } void Sdc::checkForThruHpins(ExceptionPath *exception) { ExceptionThruSeq *thrus = exception->thrus(); if (thrus) { for (ExceptionThru *thru : *thrus) { if (thru->edges()) { have_thru_hpin_exceptions_ = true; break; } } } } 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) { size_t hash = exception->hash(missing_pt); debugPrint(debug_, "exception_merge", 3, "record merge hash %zu %s missing %s", hash, exception->asString(network_), missing_pt->asString(network_)); ExceptionPathSet *set = exception_merge_hash_.findKey(hash); if (set == nullptr) { 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::recordExceptionPins(ExceptionPath *exception) { ExceptionFrom *from = exception->from(); if (from) recordExceptionPins(exception, from->pins(), pin_exceptions_); ExceptionThruSeq *thrus = exception->thrus(); if (thrus) { for (ExceptionThru *thru : *thrus) recordExceptionPins(exception, thru->pins(), pin_exceptions_); } ExceptionTo *to = exception->to(); if (to) recordExceptionPins(exception, to->pins(), pin_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_); for (ExceptionThru *thru : *exception->thrus()) 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) { if (clks) { for (Clock *clk : *clks) { ExceptionPathSet *set = exception_map.findKey(clk); if (set == nullptr) { set = new ExceptionPathSet; exception_map[clk] = set; } set->insert(exception); } } } void Sdc::recordExceptionEdges(ExceptionPath *exception, EdgePinsSet *edges, EdgeExceptionsMap &exception_map) { if (edges) { for (const EdgePins &edge : *edges) { ExceptionPathSet *set = exception_map.findKey(edge); if (set == nullptr) { set = new ExceptionPathSet; exception_map.insert(edge, set); } set->insert(exception); } } } void Sdc::recordExceptionPins(ExceptionPath *exception, PinSet *pins, PinExceptionsMap &exception_map) { if (pins) { for (const Pin *pin : *pins) { ExceptionPathSet *set = exception_map.findKey(pin); if (set == nullptr) { set = new ExceptionPathSet; exception_map.insert(pin, set); } set->insert(exception); } } } void Sdc::recordExceptionHpin(ExceptionPath *exception, Pin *pin, PinExceptionsMap &exception_map) { ExceptionPathSet *set = exception_map.findKey(pin); if (set == nullptr) { set = new ExceptionPathSet; exception_map.insert(pin, set); } set->insert(exception); } void Sdc::recordExceptionInsts(ExceptionPath *exception, InstanceSet *insts, InstanceExceptionsMap &exception_map) { if (insts) { for (const Instance *inst : *insts) { ExceptionPathSet *set = exception_map.findKey(inst); if (set == nullptr) { set = new ExceptionPathSet; exception_map[inst] = set; } set->insert(exception); } } } void Sdc::recordExceptionNets(ExceptionPath *exception, NetSet *nets, NetExceptionsMap &exception_map) { if (nets) { for (const Net *net : *nets) { ExceptionPathSet *set = exception_map.findKey(net); if (set == nullptr) { 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(); size_t hash = exception->hash(missing_pt); ExceptionPathSet *matches = exception_merge_hash_.findKey(hash); if (matches) { for (ExceptionPath *match : *matches) { 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)) { debugPrint(debug_, "exception_merge", 1, "merge %s", exception->asString(network_)); debugPrint(debug_, "exception_merge", 1, " with %s", match->asString(network_)); // Unrecord the exception that is being merged away. unrecordException(exception); unrecordMergeHashes(match); missing_pt->mergeInto(match_missing_pt, network_); 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; } return nullptr; } //////////////////////////////////////////////////////////////// void Sdc::deleteExceptions() { exceptions_.deleteContentsClear(); exception_id_ = 0; first_from_pin_exceptions_.deleteContentsClear(); first_from_clk_exceptions_.deleteContentsClear(); first_from_inst_exceptions_.deleteContentsClear(); first_to_pin_exceptions_.deleteContentsClear(); first_to_clk_exceptions_.deleteContentsClear(); first_to_inst_exceptions_.deleteContentsClear(); first_thru_pin_exceptions_.deleteContentsClear(); first_thru_inst_exceptions_.deleteContentsClear(); first_thru_net_exceptions_.deleteContentsClear(); first_thru_edge_exceptions_.deleteContentsClear(); first_thru_edge_exceptions_.clear(); path_delay_internal_from_.clear(); path_delay_internal_from_break_.clear(); path_delay_internal_to_.clear(); path_delay_internal_to_break_.clear(); pin_exceptions_.deleteContentsClear(); deleteExceptionPtHashMapSets(exception_merge_hash_); exception_merge_hash_.clear(); have_thru_hpin_exceptions_ = false; } void Sdc::deleteExceptionPtHashMapSets(ExceptionPathPtHash &map) { map.deleteContents(); } //////////////////////////////////////////////////////////////// void Sdc::deleteExceptionsReferencing(Clock *clk) { // erase prevents range iteration. 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) { debugPrint(debug_, "exception_merge", 2, "delete %s", exception->asString(network_)); unrecordException(exception); delete exception; } void Sdc::unrecordException(ExceptionPath *exception) { unrecordMergeHashes(exception); unrecordExceptionFirstPts(exception); exceptions_.erase(exception); } 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) { size_t hash = exception->hash(missing_pt); debugPrint(debug_, "exception_merge", 3, "unrecord merge hash %zu %s missing %s", hash, exception->asString(network_), missing_pt->asString(network_)); ExceptionPathSet *matches = exception_merge_hash_.findKey(hash); if (matches) matches->erase(exception); } 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) { if (clks) { for (Clock *clk : *clks) { ExceptionPathSet *set = exception_map.findKey(clk); if (set) set->erase(exception); } } } void Sdc::unrecordExceptionPins(ExceptionPath *exception, PinSet *pins, PinExceptionsMap &exception_map) { if (pins) { for (const Pin *pin : *pins) { ExceptionPathSet *set = exception_map.findKey(pin); if (set) set->erase(exception); } } } void Sdc::unrecordExceptionInsts(ExceptionPath *exception, InstanceSet *insts, InstanceExceptionsMap &exception_map) { if (insts) { for (const Instance *inst : *insts) { ExceptionPathSet *set = exception_map.findKey(inst); if (set) set->erase(exception); } } } void Sdc::unrecordExceptionEdges(ExceptionPath *exception, EdgePinsSet *edges, EdgeExceptionsMap &exception_map) { if (edges) { for (const EdgePins &edge : *edges) { ExceptionPathSet *set = exception_map.findKey(edge); if (set) set->erase(exception); } } } void Sdc::unrecordExceptionNets(ExceptionPath *exception, NetSet *nets, NetExceptionsMap &exception_map) { if (nets) { for (const Net *net : *nets) { ExceptionPathSet *set = exception_map.findKey(net); if (set) set->erase(exception); } } } void Sdc::unrecordExceptionHpin(ExceptionPath *exception, Pin *pin, PinExceptionsMap &exception_map) { ExceptionPathSet *set = exception_map.findKey(pin); if (set) set->erase(exception); } //////////////////////////////////////////////////////////////// class ExpandException : public ExpandedExceptionVisitor { public: ExpandException(ExceptionPath *exception, ExceptionPathSet &expansions, Network *network); virtual void visit(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to); private: ExceptionPathSet &expansions_; }; ExpandException::ExpandException(ExceptionPath *exception, ExceptionPathSet &expansions, Network *network) : ExpandedExceptionVisitor(exception, network), expansions_(expansions) { } void ExpandException::visit(ExceptionFrom *from, ExceptionThruSeq *thrus, ExceptionTo *to) { ExceptionFrom *from_clone = nullptr; if (from) from_clone = from->clone(network_); ExceptionThruSeq *thrus_clone = nullptr; if (thrus) { thrus_clone = new ExceptionThruSeq; for (ExceptionThru *thru : *thrus) { ExceptionThru *thru_clone = thru->clone(network_); thrus_clone->push_back(thru_clone); } } ExceptionTo *to_clone = nullptr; if (to) to_clone = to->clone(network_); 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, network_)) { debugPrint(debug_, "exception_match", 3, "reset match %s", 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, network_)) { unrecordPathDelayInternalFrom(expand); unrecordPathDelayInternalTo(expand); delete expand; } else addException(expand); } } } } //////////////////////////////////////////////////////////////// bool Sdc::exceptionFromStates(const Pin *pin, const RiseFall *rf, const Clock *clk, const RiseFall *clk_rf, const MinMax *min_max, ExceptionStateSet *&states) const { return exceptionFromStates(pin, rf, clk, clk_rf, min_max, true, states); } bool Sdc::exceptionFromStates(const Pin *pin, const RiseFall *rf, const Clock *clk, const RiseFall *clk_rf, const MinMax *min_max, bool include_filter, ExceptionStateSet *&states) const { bool srch_from = true; if (pin) { if (srch_from && !first_from_pin_exceptions_.empty()) srch_from &= exceptionFromStates(first_from_pin_exceptions_.findKey(pin), pin, rf, min_max, include_filter, states); if (srch_from && !first_thru_pin_exceptions_.empty()) srch_from &= exceptionFromStates(first_thru_pin_exceptions_.findKey(pin), pin, rf, min_max, include_filter, states); if (srch_from && (!first_from_inst_exceptions_.empty() || !first_thru_inst_exceptions_.empty())) { Instance *inst = network_->instance(pin); if (srch_from && !first_from_inst_exceptions_.empty()) srch_from &= exceptionFromStates(first_from_inst_exceptions_.findKey(inst), pin, rf, min_max, include_filter, states); if (srch_from && !first_thru_inst_exceptions_.empty()) srch_from &= exceptionFromStates(first_thru_inst_exceptions_.findKey(inst), pin, rf, min_max, include_filter, states); } } if (srch_from && clk && !first_from_clk_exceptions_.empty()) srch_from &= exceptionFromStates(first_from_clk_exceptions_.findKey(clk), pin, clk_rf, min_max, include_filter, states); if (!srch_from) { delete states; states = nullptr; } return srch_from; } bool Sdc::exceptionFromStates(const ExceptionPathSet *exceptions, const Pin *pin, const RiseFall *rf, const MinMax *min_max, bool include_filter, ExceptionStateSet *&states) const { if (exceptions) { for (ExceptionPath *exception : *exceptions) { if (exception->matches(min_max, false) && (exception->from() == nullptr || exception->from()->transition()->matches(rf)) && (include_filter || !exception->isFilter())) { ExceptionState *state = exception->firstState(); if (state->matchesNextThru(nullptr, pin, rf, min_max, network_)) // -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. if (states == nullptr) states = new ExceptionStateSet(); states->clear(); states->insert(state); // No need to examine other exceptions from this // pin/clock/instance. return false; } if (states == nullptr) states = new ExceptionStateSet(); states->insert(state); } } } return true; } void Sdc::exceptionFromClkStates(const Pin *pin, const RiseFall *rf, const Clock *clk, const RiseFall *clk_rf, const MinMax *min_max, ExceptionStateSet *&states) const { if (pin) { if (!first_from_pin_exceptions_.empty()) exceptionFromStates(first_from_pin_exceptions_.findKey(pin), nullptr, rf, min_max, true, states); if (!first_from_inst_exceptions_.empty()) { Instance *inst = network_->instance(pin); exceptionFromStates(first_from_inst_exceptions_.findKey(inst), pin, rf, min_max, true, states); } exceptionThruStates(first_thru_pin_exceptions_.findKey(pin), rf, min_max, states); } if (!first_from_clk_exceptions_.empty()) exceptionFromStates(first_from_clk_exceptions_.findKey(clk), pin, clk_rf, min_max, true, states); } void Sdc::filterRegQStates(const Pin *to_pin, const RiseFall *to_rf, const MinMax *min_max, ExceptionStateSet *&states) const { if (!first_from_pin_exceptions_.empty()) { const ExceptionPathSet *exceptions = first_from_pin_exceptions_.findKey(to_pin); if (exceptions) { for (ExceptionPath *exception : *exceptions) { // Hack for filter -from reg/Q. if (exception->isFilter() && exception->matchesFirstPt(to_rf, min_max)) { ExceptionState *state = exception->firstState(); if (states == nullptr) states = new ExceptionStateSet(); states->insert(state); } } } } } void Sdc::exceptionThruStates(const Pin *from_pin, const Pin *to_pin, const RiseFall *to_rf, const MinMax *min_max, ExceptionStateSet *&states) const { exceptionThruStates(first_thru_pin_exceptions_.findKey(to_pin), to_rf, min_max, states); if (!first_thru_edge_exceptions_.empty()) { EdgePins edge_pins(from_pin, to_pin); exceptionThruStates(first_thru_edge_exceptions_.findKey(edge_pins), to_rf, min_max, states); } if (!first_thru_inst_exceptions_.empty() && (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_rf, min_max, states); } } void Sdc::exceptionThruStates(const ExceptionPathSet *exceptions, const RiseFall *to_rf, const MinMax *min_max, // Return value. ExceptionStateSet *&states) const { if (exceptions) { for (ExceptionPath *exception : *exceptions) { if (exception->matchesFirstPt(to_rf, min_max)) { ExceptionState *state = exception->firstState(); if (states == nullptr) states = new ExceptionStateSet(); states->insert(state); } } } } //////////////////////////////////////////////////////////////// void Sdc::exceptionTo(ExceptionPathType type, const Pin *pin, const RiseFall *rf, 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_.empty()) { Instance *inst = network_->instance(pin); exceptionTo(first_to_inst_exceptions_.findKey(inst), type, pin, rf, clk_edge, min_max, match_min_max_exactly, hi_priority_exception, hi_priority); } if (!first_to_pin_exceptions_.empty()) exceptionTo(first_to_pin_exceptions_.findKey(pin), type, pin, rf, clk_edge, min_max, match_min_max_exactly, hi_priority_exception, hi_priority); if (clk_edge && !first_to_clk_exceptions_.empty()) exceptionTo(first_to_clk_exceptions_.findKey(clk_edge->clock()), type, pin, rf, 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 RiseFall *rf, 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) { for (ExceptionPath *exception : *to_exceptions) { exceptionTo(exception, type, pin, rf, clk_edge, min_max, match_min_max_exactly, hi_priority_exception, hi_priority); } } } void Sdc::exceptionTo(ExceptionPath *exception, ExceptionPathType type, const Pin *pin, const RiseFall *rf, const ClockEdge *clk_edge, const MinMax *min_max, bool match_min_max_exactly, // Return values. ExceptionPath *&hi_priority_exception, int &hi_priority) const { if ((type == ExceptionPathType::any || exception->type() == type) && exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, match_min_max_exactly, false)) { int priority = exception->priority(min_max); if (hi_priority_exception == nullptr || 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 RiseFall *rf, 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) && ((to == nullptr && !require_to_pin) || (to && to->matches(pin, clk_edge, rf, network_))); } bool Sdc::isCompleteTo(ExceptionState *state, const Pin *pin, const RiseFall *rf, const ClockEdge *clk_edge, const MinMax *min_max, bool match_min_max_exactly, bool require_to_pin) const { return state->nextThru() == nullptr && exceptionMatchesTo(state->exception(), pin, rf, clk_edge, min_max, match_min_max_exactly, require_to_pin); } bool Sdc::isCompleteTo(ExceptionState *state, const Pin *pin, const RiseFall *rf, const MinMax *min_max) const { ExceptionPath *exception = state->exception(); ExceptionTo *to = exception->to(); return state->nextThru() == nullptr && to && exception->matches(min_max, true) && to->matches(pin, rf, network_); } //////////////////////////////////////////////////////////////// void Sdc::groupPathsTo(const Pin *pin, const RiseFall *rf, const ClockEdge *clk_edge, const MinMax *min_max, // Return value. ExceptionPathSeq &group_paths) const { if (!first_to_inst_exceptions_.empty()) { Instance *inst = network_->instance(pin); groupPathsTo(first_to_inst_exceptions_.findKey(inst), pin, rf, clk_edge, min_max, group_paths); } if (!first_to_pin_exceptions_.empty()) groupPathsTo(first_to_pin_exceptions_.findKey(pin), pin, rf, clk_edge, min_max, group_paths); if (clk_edge && !first_to_clk_exceptions_.empty()) groupPathsTo(first_to_clk_exceptions_.findKey(clk_edge->clock()), pin, rf, clk_edge, min_max, group_paths); } void Sdc::groupPathsTo(const ExceptionPathSet *to_exceptions, const Pin *pin, const RiseFall *rf, const ClockEdge *clk_edge, const MinMax *min_max, // Return value. ExceptionPathSeq &group_paths) const { if (to_exceptions) { for (ExceptionPath *exception : *to_exceptions) { if (exception->isGroupPath() && exceptionMatchesTo(exception, pin, rf, clk_edge, min_max, true, false)) group_paths.push_back(exception); } } } //////////////////////////////////////////////////////////////// Wireload * Sdc::wireload(const MinMax *min_max) { return wireload_[min_max->index()]; } void Sdc::setWireload(Wireload *wireload, const MinMaxAll *min_max) { for (auto mm_index : min_max->rangeIndex()) wireload_[mm_index] = wireload; } 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()]; if (sel == nullptr) { // 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) { for (auto mm_index : min_max->rangeIndex()) wireload_selection_[mm_index] = selection; } //////////////////////////////////////////////////////////////// void Sdc::connectPinAfter(const Pin *pin) { if (have_thru_hpin_exceptions_) { PinSet *drvrs = network_->drivers(pin); for (ExceptionPath *exception : exceptions_) { ExceptionPt *first_pt = exception->firstPt(); ExceptionThruSeq *thrus = exception->thrus(); if (thrus) { for (ExceptionThru *thru : *exception->thrus()) { if (thru->edges()) { thru->connectPinAfter(drvrs, network_); if (first_pt == thru) recordExceptionEdges(exception, thru->edges(), first_thru_edge_exceptions_); } } } } } } void Sdc::disconnectPinBefore(const Pin *pin) { auto itr = pin_exceptions_.find(pin); if (itr != pin_exceptions_.end()) { for (ExceptionPath *exception : *itr->second) { ExceptionFrom *from = exception->from(); if (from) from->disconnectPinBefore(pin, network_); ExceptionTo *to = exception->to(); if (to) to->disconnectPinBefore(pin, network_); ExceptionPt *first_pt = exception->firstPt(); ExceptionThruSeq *thrus = exception->thrus(); if (thrus) { for (ExceptionThru *thru : *exception->thrus()) { thru->disconnectPinBefore(pin, network_); if (thru == first_pt) recordExceptionEdges(exception, thru->edges(), first_thru_edge_exceptions_); } } } first_from_pin_exceptions_.erase(pin); first_thru_pin_exceptions_.erase(pin); first_to_pin_exceptions_.erase(pin); pin_exceptions_.erase(pin); } for (int corner_index = 0; corner_index < corners_->count(); corner_index++) drvr_pin_wire_cap_maps_[corner_index].erase(pin); } void Sdc::clkHpinDisablesChanged(const Pin *pin) { if (isLeafPinClock(pin)) clkHpinDisablesInvalid(); } //////////////////////////////////////////////////////////////// // Find the leaf load pins corresponding to pin. // If the pin is hierarchical, the leaf pins are: // hierarchical input - load pins inside the hierarchical instance // hierarchical output - load pins outside the hierarchical instance void findLeafLoadPins(const Pin *pin, const Network *network, PinSet *leaf_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()) { const Pin *pin1 = pin_iter->next(); bool is_inside = network->isInside(pin1, hinst); if (((is_input && is_inside) || (is_output && !is_inside)) && network->isLoad(pin1)) leaf_pins->insert(pin1); } delete pin_iter; } else leaf_pins->insert(pin); } // Find the leaf driver pins corresponding to pin. // If the pin is hierarchical, the leaf pins are: // hierarchical input - driver pins outside the hierarchical instance // hierarchical output - driver pins inside the hierarchical instance void findLeafDriverPins(const Pin *pin, const Network *network, PinSet *leaf_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()) { const Pin *pin1 = pin_iter->next(); bool is_inside = network->isInside(pin1, hinst); if (((is_input && !is_inside) || (is_output && is_inside)) && network->isDriver(pin1)) leaf_pins->insert(pin1); } delete pin_iter; } else leaf_pins->insert(pin); } //////////////////////////////////////////////////////////////// NetWireCaps::NetWireCaps() : subtract_pin_cap_{false, false} { } bool NetWireCaps::subtractPinCap(const MinMax *min_max) { return subtract_pin_cap_[min_max->index()]; } void NetWireCaps::setSubtractPinCap(bool subtrace_pin_cap, const MinMax *min_max) { subtract_pin_cap_[min_max->index()] = subtrace_pin_cap; } } // namespace