Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
7b4cea532b
|
|
@ -202,7 +202,6 @@ set(STA_SOURCE
|
||||||
search/TagGroup.cc
|
search/TagGroup.cc
|
||||||
search/VertexVisitor.cc
|
search/VertexVisitor.cc
|
||||||
search/VisitPathEnds.cc
|
search/VisitPathEnds.cc
|
||||||
search/VisitPathGroupVertices.cc
|
|
||||||
search/WorstSlack.cc
|
search/WorstSlack.cc
|
||||||
|
|
||||||
spice/WritePathSpice.cc
|
spice/WritePathSpice.cc
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,9 @@ The following classes now return const objects.
|
||||||
Transition
|
Transition
|
||||||
TimingRole
|
TimingRole
|
||||||
|
|
||||||
|
Liberty PgPorts are now LibertyPorts with additional member functions for
|
||||||
|
liberty pg_pins.
|
||||||
|
|
||||||
Release 2.6.1 2025/03/??
|
Release 2.6.1 2025/03/??
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ to remove paths through identical pins and rise/fall edges.
|
||||||
|
|
||||||
report_checks [-unique_edges_to_endpoint]
|
report_checks [-unique_edges_to_endpoint]
|
||||||
|
|
||||||
|
Instances now have pins for verilog netlist power/ground connections,
|
||||||
|
|
||||||
Release 2.6.1 2025/03/30
|
Release 2.6.1 2025/03/30
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ class WriteTimingModel;
|
||||||
class LibertyCellIterator;
|
class LibertyCellIterator;
|
||||||
class LibertyCellPortIterator;
|
class LibertyCellPortIterator;
|
||||||
class LibertyCellPortBitIterator;
|
class LibertyCellPortBitIterator;
|
||||||
class LibertyCellPgPortIterator;
|
|
||||||
class LibertyPortMemberIterator;
|
class LibertyPortMemberIterator;
|
||||||
class ModeValueDef;
|
class ModeValueDef;
|
||||||
class TestCell;
|
class TestCell;
|
||||||
|
|
@ -56,7 +55,6 @@ class LibertyReader;
|
||||||
class OcvDerate;
|
class OcvDerate;
|
||||||
class TimingArcAttrs;
|
class TimingArcAttrs;
|
||||||
class InternalPowerAttrs;
|
class InternalPowerAttrs;
|
||||||
class LibertyPgPort;
|
|
||||||
class StaState;
|
class StaState;
|
||||||
class Corner;
|
class Corner;
|
||||||
class Corners;
|
class Corners;
|
||||||
|
|
@ -90,7 +88,6 @@ typedef Vector<LatchEnable*> LatchEnableSeq;
|
||||||
typedef Map<const char *, OcvDerate*, CharPtrLess> OcvDerateMap;
|
typedef Map<const char *, OcvDerate*, CharPtrLess> OcvDerateMap;
|
||||||
typedef Vector<InternalPowerAttrs*> InternalPowerAttrsSeq;
|
typedef Vector<InternalPowerAttrs*> InternalPowerAttrsSeq;
|
||||||
typedef Map<std::string, float> SupplyVoltageMap;
|
typedef Map<std::string, float> SupplyVoltageMap;
|
||||||
typedef Map<std::string, LibertyPgPort*> LibertyPgPortMap;
|
|
||||||
typedef Map<std::string, DriverWaveform*> DriverWaveformMap;
|
typedef Map<std::string, DriverWaveform*> DriverWaveformMap;
|
||||||
typedef Vector<DcalcAnalysisPt*> DcalcAnalysisPtSeq;
|
typedef Vector<DcalcAnalysisPt*> DcalcAnalysisPtSeq;
|
||||||
|
|
||||||
|
|
@ -100,6 +97,12 @@ enum class DelayModelType { cmos_linear, cmos_pwl, cmos2, table, polynomial, dcm
|
||||||
|
|
||||||
enum class ScanSignalType { enable, enable_inverted, clock, clock_a, clock_b,
|
enum class ScanSignalType { enable, enable_inverted, clock, clock_a, clock_b,
|
||||||
input, input_inverted, output, output_inverted, none };
|
input, input_inverted, output, output_inverted, none };
|
||||||
|
enum class PwrGndType { none,
|
||||||
|
primary_power, primary_ground,
|
||||||
|
backup_power, backup_ground,
|
||||||
|
internal_power, internal_ground,
|
||||||
|
nwell, pwell,
|
||||||
|
deepnwell, deeppwell};
|
||||||
|
|
||||||
enum class ScaleFactorPvt { process, volt, temp, unknown };
|
enum class ScaleFactorPvt { process, volt, temp, unknown };
|
||||||
constexpr int scale_factor_pvt_count = int(ScaleFactorPvt::unknown) + 1;
|
constexpr int scale_factor_pvt_count = int(ScaleFactorPvt::unknown) + 1;
|
||||||
|
|
@ -423,8 +426,6 @@ public:
|
||||||
LibertyPort *findLibertyPort(const char *name) const;
|
LibertyPort *findLibertyPort(const char *name) const;
|
||||||
LibertyPortSeq findLibertyPortsMatching(PatternMatch *pattern) const;
|
LibertyPortSeq findLibertyPortsMatching(PatternMatch *pattern) const;
|
||||||
bool hasInternalPorts() const { return has_internal_ports_; }
|
bool hasInternalPorts() const { return has_internal_ports_; }
|
||||||
LibertyPgPort *findPgPort(const char *name) const;
|
|
||||||
size_t pgPortCount() const { return pg_port_map_.size(); }
|
|
||||||
ScaleFactors *scaleFactors() const { return scale_factors_; }
|
ScaleFactors *scaleFactors() const { return scale_factors_; }
|
||||||
void setScaleFactors(ScaleFactors *scale_factors);
|
void setScaleFactors(ScaleFactors *scale_factors);
|
||||||
ModeDef *makeModeDef(const char *name);
|
ModeDef *makeModeDef(const char *name);
|
||||||
|
|
@ -533,7 +534,6 @@ public:
|
||||||
void setOcvArcDepth(float depth);
|
void setOcvArcDepth(float depth);
|
||||||
void setOcvDerate(OcvDerate *derate);
|
void setOcvDerate(OcvDerate *derate);
|
||||||
void addOcvDerate(OcvDerate *derate);
|
void addOcvDerate(OcvDerate *derate);
|
||||||
void addPgPort(LibertyPgPort *pg_port);
|
|
||||||
void setTestCell(TestCell *test);
|
void setTestCell(TestCell *test);
|
||||||
void setHasInferedRegTimingArcs(bool infered);
|
void setHasInferedRegTimingArcs(bool infered);
|
||||||
void setIsDisabledConstraint(bool is_disabled);
|
void setIsDisabledConstraint(bool is_disabled);
|
||||||
|
|
@ -643,7 +643,6 @@ protected:
|
||||||
Vector<LibertyCell*> corner_cells_;
|
Vector<LibertyCell*> corner_cells_;
|
||||||
float leakage_power_;
|
float leakage_power_;
|
||||||
bool leakage_power_exists_;
|
bool leakage_power_exists_;
|
||||||
LibertyPgPortMap pg_port_map_;
|
|
||||||
bool has_internal_ports_;
|
bool has_internal_ports_;
|
||||||
std::atomic<bool> have_voltage_waveforms_;
|
std::atomic<bool> have_voltage_waveforms_;
|
||||||
std::mutex waveform_lock_;
|
std::mutex waveform_lock_;
|
||||||
|
|
@ -653,7 +652,6 @@ protected:
|
||||||
private:
|
private:
|
||||||
friend class LibertyLibrary;
|
friend class LibertyLibrary;
|
||||||
friend class LibertyCellPortIterator;
|
friend class LibertyCellPortIterator;
|
||||||
friend class LibertyCellPgPortIterator;
|
|
||||||
friend class LibertyPort;
|
friend class LibertyPort;
|
||||||
friend class LibertyBuilder;
|
friend class LibertyBuilder;
|
||||||
};
|
};
|
||||||
|
|
@ -681,17 +679,6 @@ private:
|
||||||
ConcreteCellPortBitIterator *iter_;
|
ConcreteCellPortBitIterator *iter_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LibertyCellPgPortIterator : public Iterator<LibertyPgPort*>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LibertyCellPgPortIterator(const LibertyCell *cell);
|
|
||||||
bool hasNext();
|
|
||||||
LibertyPgPort *next();
|
|
||||||
|
|
||||||
private:
|
|
||||||
LibertyPgPortMap::Iterator iter_;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class LibertyPort : public ConcretePort
|
class LibertyPort : public ConcretePort
|
||||||
|
|
@ -704,6 +691,16 @@ public:
|
||||||
LibertyPort *bundlePort() const;
|
LibertyPort *bundlePort() const;
|
||||||
BusDcl *busDcl() const { return bus_dcl_; }
|
BusDcl *busDcl() const { return bus_dcl_; }
|
||||||
void setDirection(PortDirection *dir);
|
void setDirection(PortDirection *dir);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// pg_pin functions
|
||||||
|
bool isPwrGnd() const;
|
||||||
|
PwrGndType pwrGndType() const { return pwr_gnd_type_; }
|
||||||
|
void setPwrGndType(PwrGndType type);
|
||||||
|
const char *voltageName() const { return voltage_name_.c_str(); }
|
||||||
|
void setVoltageName(const char *voltage_name);
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
ScanSignalType scanSignalType() const { return scan_signal_type_; }
|
ScanSignalType scanSignalType() const { return scan_signal_type_; }
|
||||||
void setScanSignalType(ScanSignalType type);
|
void setScanSignalType(ScanSignalType type);
|
||||||
void fanoutLoad(// Return values.
|
void fanoutLoad(// Return values.
|
||||||
|
|
@ -887,8 +884,10 @@ protected:
|
||||||
|
|
||||||
LibertyCell *liberty_cell_;
|
LibertyCell *liberty_cell_;
|
||||||
BusDcl *bus_dcl_;
|
BusDcl *bus_dcl_;
|
||||||
FuncExpr *function_;
|
PwrGndType pwr_gnd_type_;
|
||||||
|
std::string voltage_name_;
|
||||||
ScanSignalType scan_signal_type_;
|
ScanSignalType scan_signal_type_;
|
||||||
|
FuncExpr *function_;
|
||||||
FuncExpr *tristate_enable_;
|
FuncExpr *tristate_enable_;
|
||||||
ScaledPortMap *scaled_ports_;
|
ScaledPortMap *scaled_ports_;
|
||||||
RiseFallMinMax capacitance_;
|
RiseFallMinMax capacitance_;
|
||||||
|
|
@ -1136,37 +1135,13 @@ private:
|
||||||
TablePtr derate_[RiseFall::index_count][EarlyLate::index_count][path_type_count];
|
TablePtr derate_[RiseFall::index_count][EarlyLate::index_count][path_type_count];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Power/ground port.
|
|
||||||
class LibertyPgPort
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum PgType { unknown,
|
|
||||||
primary_power, primary_ground,
|
|
||||||
backup_power, backup_ground,
|
|
||||||
internal_power, internal_ground,
|
|
||||||
nwell, pwell,
|
|
||||||
deepnwell, deeppwell};
|
|
||||||
LibertyPgPort(const char *name,
|
|
||||||
LibertyCell *cell);
|
|
||||||
const char *name() const { return name_.c_str(); }
|
|
||||||
LibertyCell *cell() const { return cell_; }
|
|
||||||
PgType pgType() const { return pg_type_; }
|
|
||||||
void setPgType(PgType type);
|
|
||||||
const char *voltageName() const { return voltage_name_.c_str(); }
|
|
||||||
void setVoltageName(const char *voltage_name);
|
|
||||||
static bool equiv(const LibertyPgPort *port1,
|
|
||||||
const LibertyPgPort *port2);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string name_;
|
|
||||||
PgType pg_type_;
|
|
||||||
std::string voltage_name_;
|
|
||||||
LibertyCell *cell_;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
portLibertyToSta(const char *port_name);
|
portLibertyToSta(const char *port_name);
|
||||||
const char *
|
const char *
|
||||||
scanSignalTypeName(ScanSignalType scan_type);
|
scanSignalTypeName(ScanSignalType scan_type);
|
||||||
|
const char *
|
||||||
|
pwrGndTypeName(PwrGndType pwr_gnd_type);
|
||||||
|
PwrGndType
|
||||||
|
findPwrGndType(const char *pg_name);
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
||||||
|
|
@ -136,15 +136,15 @@ typedef Set<InputDelay*> InputDelaySet;
|
||||||
typedef Map<const Pin*, InputDelaySet*, PinIdLess> InputDelaysPinMap;
|
typedef Map<const Pin*, InputDelaySet*, PinIdLess> InputDelaysPinMap;
|
||||||
typedef Set<OutputDelay*> OutputDelaySet;
|
typedef Set<OutputDelay*> OutputDelaySet;
|
||||||
typedef Map<const Pin*,OutputDelaySet*, PinIdLess> OutputDelaysPinMap;
|
typedef Map<const Pin*,OutputDelaySet*, PinIdLess> OutputDelaysPinMap;
|
||||||
typedef UnorderedMap<const Pin*,ExceptionPathSet*> PinExceptionsMap;
|
typedef UnorderedMap<const Pin*,ExceptionPathSet> PinExceptionsMap;
|
||||||
typedef Map<const Clock*,ExceptionPathSet*> ClockExceptionsMap;
|
typedef UnorderedMap<const Clock*,ExceptionPathSet> ClockExceptionsMap;
|
||||||
typedef Map<const Instance*,ExceptionPathSet*> InstanceExceptionsMap;
|
typedef UnorderedMap<const Instance*,ExceptionPathSet> InstanceExceptionsMap;
|
||||||
typedef Map<const Net*,ExceptionPathSet*> NetExceptionsMap;
|
typedef UnorderedMap<const Net*,ExceptionPathSet> NetExceptionsMap;
|
||||||
typedef UnorderedMap<EdgePins, ExceptionPathSet*,
|
typedef UnorderedMap<EdgePins, ExceptionPathSet,
|
||||||
PinPairHash, PinPairEqual> EdgeExceptionsMap;
|
PinPairHash, PinPairEqual> EdgeExceptionsMap;
|
||||||
typedef Vector<ExceptionThru*> ExceptionThruSeq;
|
typedef Vector<ExceptionThru*> ExceptionThruSeq;
|
||||||
typedef Map<const Port*,InputDrive*> InputDriveMap;
|
typedef Map<const Port*,InputDrive*> InputDriveMap;
|
||||||
typedef Map<size_t, ExceptionPathSet*, std::less<size_t> > ExceptionPathPtHash;
|
typedef Map<size_t, ExceptionPathSet, std::less<size_t>> ExceptionPathPtHash;
|
||||||
typedef Set<ClockLatency*, ClockLatencyLess> ClockLatencies;
|
typedef Set<ClockLatency*, ClockLatencyLess> ClockLatencies;
|
||||||
typedef Map<const Pin*, ClockUncertainties*> PinClockUncertaintyMap;
|
typedef Map<const Pin*, ClockUncertainties*> PinClockUncertaintyMap;
|
||||||
typedef Set<InterClockUncertainty*, InterClockUncertaintyLess> InterClockUncertaintySet;
|
typedef Set<InterClockUncertainty*, InterClockUncertaintyLess> InterClockUncertaintySet;
|
||||||
|
|
@ -1018,7 +1018,7 @@ public:
|
||||||
const PinSet &pathDelayInternalFrom() const;
|
const PinSet &pathDelayInternalFrom() const;
|
||||||
bool isPathDelayInternalTo(const Pin *pin) const;
|
bool isPathDelayInternalTo(const Pin *pin) const;
|
||||||
bool isPathDelayInternalToBreak(const Pin *pin) const;
|
bool isPathDelayInternalToBreak(const Pin *pin) const;
|
||||||
ExceptionPathSet *exceptions() { return &exceptions_; }
|
ExceptionPathSet &exceptions() { return exceptions_; }
|
||||||
void deleteExceptions();
|
void deleteExceptions();
|
||||||
void deleteException(ExceptionPath *exception);
|
void deleteException(ExceptionPath *exception);
|
||||||
void recordException(ExceptionPath *exception);
|
void recordException(ExceptionPath *exception);
|
||||||
|
|
@ -1043,7 +1043,6 @@ protected:
|
||||||
void removeLibertyAnnotations();
|
void removeLibertyAnnotations();
|
||||||
void deleteExceptionsReferencing(Clock *clk);
|
void deleteExceptionsReferencing(Clock *clk);
|
||||||
void deleteClkPinMappings(Clock *clk);
|
void deleteClkPinMappings(Clock *clk);
|
||||||
void deleteExceptionPtHashMapSets(ExceptionPathPtHash &map);
|
|
||||||
void makeClkPinMappings(Clock *clk);
|
void makeClkPinMappings(Clock *clk);
|
||||||
void deletePinClocks(Clock *defining_clk,
|
void deletePinClocks(Clock *defining_clk,
|
||||||
PinSet *pins);
|
PinSet *pins);
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ typedef Set<LibertyPortPair, LibertyPortPairLess> LibertyPortPairSet;
|
||||||
typedef Map<const Instance*, DisabledInstancePorts*> DisabledInstancePortsMap;
|
typedef Map<const Instance*, DisabledInstancePorts*> DisabledInstancePortsMap;
|
||||||
typedef Map<LibertyCell*, DisabledCellPorts*> DisabledCellPortsMap;
|
typedef Map<LibertyCell*, DisabledCellPorts*> DisabledCellPortsMap;
|
||||||
typedef MinMaxValues<float> ClockUncertainties;
|
typedef MinMaxValues<float> ClockUncertainties;
|
||||||
typedef Set<ExceptionPath*> ExceptionPathSet;
|
typedef std::set<ExceptionPath*> ExceptionPathSet;
|
||||||
typedef PinPair EdgePins;
|
typedef PinPair EdgePins;
|
||||||
typedef PinPairSet EdgePinsSet;
|
typedef PinPairSet EdgePinsSet;
|
||||||
typedef Map<const Pin*, LogicValue> LogicValueMap;
|
typedef Map<const Pin*, LogicValue> LogicValueMap;
|
||||||
|
|
|
||||||
|
|
@ -907,9 +907,6 @@ public:
|
||||||
PinSet endpointPins();
|
PinSet endpointPins();
|
||||||
VertexSet *endpoints();
|
VertexSet *endpoints();
|
||||||
int endpointViolationCount(const MinMax *min_max);
|
int endpointViolationCount(const MinMax *min_max);
|
||||||
// Find the fanin vertices for a group path.
|
|
||||||
// Vertices in the clock network are NOT included.
|
|
||||||
PinSet findGroupPathPins(const char *group_path_name);
|
|
||||||
// Find all required times after updateTiming().
|
// Find all required times after updateTiming().
|
||||||
void findRequireds();
|
void findRequireds();
|
||||||
std::string reportDelayCalc(Edge *edge,
|
std::string reportDelayCalc(Edge *edge,
|
||||||
|
|
|
||||||
|
|
@ -67,17 +67,9 @@ static unsigned
|
||||||
hashFuncExpr(const FuncExpr *expr);
|
hashFuncExpr(const FuncExpr *expr);
|
||||||
static unsigned
|
static unsigned
|
||||||
hashPort(const LibertyPort *port);
|
hashPort(const LibertyPort *port);
|
||||||
static unsigned
|
|
||||||
hashCellPgPorts(const LibertyCell *cell);
|
|
||||||
static unsigned
|
|
||||||
hashPgPort(const LibertyPgPort *port);
|
|
||||||
static bool
|
static bool
|
||||||
cellHasFuncs(const LibertyCell *cell);
|
cellHasFuncs(const LibertyCell *cell);
|
||||||
|
|
||||||
static bool
|
|
||||||
equivCellPgPorts(const LibertyCell *cell1,
|
|
||||||
const LibertyCell *cell2);
|
|
||||||
|
|
||||||
static float
|
static float
|
||||||
cellDriveResistance(const LibertyCell *cell)
|
cellDriveResistance(const LibertyCell *cell)
|
||||||
{
|
{
|
||||||
|
|
@ -201,7 +193,6 @@ static unsigned
|
||||||
hashCell(const LibertyCell *cell)
|
hashCell(const LibertyCell *cell)
|
||||||
{
|
{
|
||||||
return hashCellPorts(cell)
|
return hashCellPorts(cell)
|
||||||
+ hashCellPgPorts(cell)
|
|
||||||
+ hashCellSequentials(cell);
|
+ hashCellSequentials(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -226,25 +217,6 @@ hashPort(const LibertyPort *port)
|
||||||
+ port->direction()->index() * 5;
|
+ port->direction()->index() * 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned
|
|
||||||
hashCellPgPorts(const LibertyCell *cell)
|
|
||||||
{
|
|
||||||
unsigned hash = 0;
|
|
||||||
LibertyCellPgPortIterator port_iter(cell);
|
|
||||||
while (port_iter.hasNext()) {
|
|
||||||
LibertyPgPort *port = port_iter.next();
|
|
||||||
hash += hashPgPort(port);
|
|
||||||
}
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned
|
|
||||||
hashPgPort(const LibertyPgPort *port)
|
|
||||||
{
|
|
||||||
return hashString(port->name()) * 3
|
|
||||||
+ static_cast<int>(port->pgType()) * 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned
|
static unsigned
|
||||||
hashCellSequentials(const LibertyCell *cell)
|
hashCellSequentials(const LibertyCell *cell)
|
||||||
{
|
{
|
||||||
|
|
@ -333,7 +305,6 @@ equivCells(const LibertyCell *cell1,
|
||||||
{
|
{
|
||||||
return equivCellPorts(cell1, cell2)
|
return equivCellPorts(cell1, cell2)
|
||||||
&& equivCellFuncs(cell1, cell2)
|
&& equivCellFuncs(cell1, cell2)
|
||||||
&& equivCellPgPorts(cell1, cell2)
|
|
||||||
&& equivCellSequentials(cell1, cell2)
|
&& equivCellSequentials(cell1, cell2)
|
||||||
&& equivCellStatetables(cell1, cell2)
|
&& equivCellStatetables(cell1, cell2)
|
||||||
// Reqwuire timing arc equivalence if there are no functions.
|
// Reqwuire timing arc equivalence if there are no functions.
|
||||||
|
|
@ -347,7 +318,6 @@ equivCellsArcs(const LibertyCell *cell1,
|
||||||
{
|
{
|
||||||
return equivCellPorts(cell1, cell2)
|
return equivCellPorts(cell1, cell2)
|
||||||
&& equivCellFuncs(cell1, cell2)
|
&& equivCellFuncs(cell1, cell2)
|
||||||
&& equivCellPgPorts(cell1, cell2)
|
|
||||||
&& equivCellSequentials(cell1, cell2)
|
&& equivCellSequentials(cell1, cell2)
|
||||||
&& equivCellStatetables(cell1, cell2)
|
&& equivCellStatetables(cell1, cell2)
|
||||||
// Reqwuire timing arc equivalence if there are no functions.
|
// Reqwuire timing arc equivalence if there are no functions.
|
||||||
|
|
@ -403,25 +373,6 @@ equivCellPorts(const LibertyCell *cell1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
equivCellPgPorts(const LibertyCell *cell1,
|
|
||||||
const LibertyCell *cell2)
|
|
||||||
{
|
|
||||||
if (cell1->pgPortCount() != cell2->pgPortCount())
|
|
||||||
return false;
|
|
||||||
else {
|
|
||||||
LibertyCellPgPortIterator port_iter1(cell1);
|
|
||||||
while (port_iter1.hasNext()) {
|
|
||||||
LibertyPgPort *port1 = port_iter1.next();
|
|
||||||
const char* name = port1->name();
|
|
||||||
LibertyPgPort *port2 = cell2->findPgPort(name);
|
|
||||||
if (!(port2 && LibertyPgPort::equiv(port1, port2)))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
equivCellSequentials(const LibertyCell *cell1,
|
equivCellSequentials(const LibertyCell *cell1,
|
||||||
const LibertyCell *cell2)
|
const LibertyCell *cell2)
|
||||||
|
|
|
||||||
|
|
@ -975,8 +975,6 @@ LibertyCell::~LibertyCell()
|
||||||
|
|
||||||
delete test_cell_;
|
delete test_cell_;
|
||||||
ocv_derate_map_.deleteContents();
|
ocv_derate_map_.deleteContents();
|
||||||
|
|
||||||
pg_port_map_.deleteContents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LibertyPort *
|
LibertyPort *
|
||||||
|
|
@ -1021,18 +1019,6 @@ LibertyCell::setHasInternalPorts(bool has_internal)
|
||||||
has_internal_ports_ = has_internal;
|
has_internal_ports_ = has_internal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
LibertyCell::addPgPort(LibertyPgPort *pg_port)
|
|
||||||
{
|
|
||||||
pg_port_map_[pg_port->name()] = pg_port;
|
|
||||||
}
|
|
||||||
|
|
||||||
LibertyPgPort *
|
|
||||||
LibertyCell::findPgPort(const char *name) const
|
|
||||||
{
|
|
||||||
return pg_port_map_.findKey(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
ModeDef *
|
ModeDef *
|
||||||
LibertyCell::makeModeDef(const char *name)
|
LibertyCell::makeModeDef(const char *name)
|
||||||
{
|
{
|
||||||
|
|
@ -2093,8 +2079,9 @@ LibertyPort::LibertyPort(LibertyCell *cell,
|
||||||
ConcretePort(name, is_bus, from_index, to_index, is_bundle, members, cell),
|
ConcretePort(name, is_bus, from_index, to_index, is_bundle, members, cell),
|
||||||
liberty_cell_(cell),
|
liberty_cell_(cell),
|
||||||
bus_dcl_(bus_dcl),
|
bus_dcl_(bus_dcl),
|
||||||
function_(nullptr),
|
pwr_gnd_type_(PwrGndType::none),
|
||||||
scan_signal_type_(ScanSignalType::none),
|
scan_signal_type_(ScanSignalType::none),
|
||||||
|
function_(nullptr),
|
||||||
tristate_enable_(nullptr),
|
tristate_enable_(nullptr),
|
||||||
scaled_ports_(nullptr),
|
scaled_ports_(nullptr),
|
||||||
fanout_load_(0.0),
|
fanout_load_(0.0),
|
||||||
|
|
@ -2168,6 +2155,50 @@ static EnumNameMap<ScanSignalType> scan_signal_type_map =
|
||||||
{ScanSignalType::output_inverted, "output_inverted"},
|
{ScanSignalType::output_inverted, "output_inverted"},
|
||||||
{ScanSignalType::none, "none"}};
|
{ScanSignalType::none, "none"}};
|
||||||
|
|
||||||
|
bool
|
||||||
|
LibertyPort::isPwrGnd() const
|
||||||
|
{
|
||||||
|
return pwr_gnd_type_ != PwrGndType::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LibertyPort::setPwrGndType(PwrGndType type)
|
||||||
|
{
|
||||||
|
pwr_gnd_type_ = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LibertyPort::setVoltageName(const char *voltage_name)
|
||||||
|
{
|
||||||
|
voltage_name_ = voltage_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EnumNameMap<PwrGndType> pwr_gnd_type_map =
|
||||||
|
{{PwrGndType::none, "node"},
|
||||||
|
{PwrGndType::primary_power, "primary_power"},
|
||||||
|
{PwrGndType::primary_ground, "primary_ground"},
|
||||||
|
{PwrGndType::backup_power, "backup_power"},
|
||||||
|
{PwrGndType::backup_ground, "backup_ground"},
|
||||||
|
{PwrGndType::internal_power, "internal_power"},
|
||||||
|
{PwrGndType::internal_ground, "internal_ground"},
|
||||||
|
{PwrGndType::nwell, "nwell"},
|
||||||
|
{PwrGndType::pwell, "pwell"},
|
||||||
|
{PwrGndType::deepnwell, "deepnwell"},
|
||||||
|
{PwrGndType::deeppwell, "deeppwell"}};
|
||||||
|
|
||||||
|
const char *
|
||||||
|
pwrGndTypeName(PwrGndType pg_type)
|
||||||
|
{
|
||||||
|
return pwr_gnd_type_map.find(pg_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
PwrGndType
|
||||||
|
findPwrGndType(const char *pg_name)
|
||||||
|
{
|
||||||
|
return pwr_gnd_type_map.find(pg_name, PwrGndType::none);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
scanSignalTypeName(ScanSignalType scan_type)
|
scanSignalTypeName(ScanSignalType scan_type)
|
||||||
|
|
@ -2526,7 +2557,8 @@ LibertyPort::equiv(const LibertyPort *port1,
|
||||||
return (port1 == nullptr && port2 == nullptr)
|
return (port1 == nullptr && port2 == nullptr)
|
||||||
|| (port1 != nullptr && port2 != nullptr
|
|| (port1 != nullptr && port2 != nullptr
|
||||||
&& stringEq(port1->name(), port2->name())
|
&& stringEq(port1->name(), port2->name())
|
||||||
&& port1->direction() == port2->direction());
|
&& port1->direction() == port2->direction()
|
||||||
|
&& port1->pwr_gnd_type_ == port2->pwr_gnd_type_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -3330,56 +3362,4 @@ OcvDerate::setDerateTable(const RiseFall *rf,
|
||||||
derate_[rf->index()][early_late->index()][int(path_type)] = derate;
|
derate_[rf->index()][early_late->index()][int(path_type)] = derate;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
LibertyPgPort::LibertyPgPort(const char *name,
|
|
||||||
LibertyCell *cell) :
|
|
||||||
name_(name),
|
|
||||||
pg_type_(unknown),
|
|
||||||
cell_(cell)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
LibertyPgPort::setPgType(PgType type)
|
|
||||||
{
|
|
||||||
pg_type_ = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
LibertyPgPort::setVoltageName(const char *voltage_name)
|
|
||||||
{
|
|
||||||
voltage_name_ = voltage_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
LibertyPgPort::equiv(const LibertyPgPort *port1,
|
|
||||||
const LibertyPgPort *port2)
|
|
||||||
{
|
|
||||||
return port1->name_ == port2->name_
|
|
||||||
&& port1->pg_type_ == port2->pg_type_;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
LibertyCellPgPortIterator::LibertyCellPgPortIterator(const LibertyCell *cell) :
|
|
||||||
iter_(const_cast<LibertyCell*>(cell)->pg_port_map_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
LibertyCellPgPortIterator::hasNext()
|
|
||||||
{
|
|
||||||
return iter_.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
LibertyPgPort *
|
|
||||||
LibertyCellPgPortIterator::next()
|
|
||||||
{
|
|
||||||
string name;
|
|
||||||
LibertyPgPort *port;
|
|
||||||
iter_.next(name, port);
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
||||||
|
|
@ -317,6 +317,7 @@ bool has_members() { return self->hasMembers(); }
|
||||||
LibertyPortMemberIterator *
|
LibertyPortMemberIterator *
|
||||||
member_iterator() { return new LibertyPortMemberIterator(self); }
|
member_iterator() { return new LibertyPortMemberIterator(self); }
|
||||||
LibertyPort *bundle_port() { return self->bundlePort(); }
|
LibertyPort *bundle_port() { return self->bundlePort(); }
|
||||||
|
bool is_pwr_gnd() { return self->isPwrGnd(); }
|
||||||
|
|
||||||
string
|
string
|
||||||
function()
|
function()
|
||||||
|
|
|
||||||
|
|
@ -561,6 +561,8 @@ LibertyReader::defineVisitors()
|
||||||
&LibertyReader::endEcsmWaveform);
|
&LibertyReader::endEcsmWaveform);
|
||||||
defineGroupVisitor("ecsm_waveform_set", &LibertyReader::beginEcsmWaveform,
|
defineGroupVisitor("ecsm_waveform_set", &LibertyReader::beginEcsmWaveform,
|
||||||
&LibertyReader::endEcsmWaveform);
|
&LibertyReader::endEcsmWaveform);
|
||||||
|
defineGroupVisitor("ecsm_capacitance", &LibertyReader::beginEcsmWaveform,
|
||||||
|
&LibertyReader::endEcsmWaveform);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -5686,8 +5688,7 @@ LibertyReader::beginPgPin(LibertyGroup *group)
|
||||||
{
|
{
|
||||||
if (cell_) {
|
if (cell_) {
|
||||||
const char *name = group->firstName();
|
const char *name = group->firstName();
|
||||||
pg_port_ = new LibertyPgPort(name, cell_);
|
pg_port_ = builder_.makePort(cell_, name);
|
||||||
cell_->addPgPort(pg_port_);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5702,35 +5703,27 @@ LibertyReader::visitPgType(LibertyAttr *attr)
|
||||||
{
|
{
|
||||||
if (pg_port_) {
|
if (pg_port_) {
|
||||||
const char *type_name = getAttrString(attr);
|
const char *type_name = getAttrString(attr);
|
||||||
LibertyPgPort::PgType type = LibertyPgPort::PgType::unknown;
|
PwrGndType type = findPwrGndType(type_name);
|
||||||
if (stringEqual(type_name, "primary_ground"))
|
PortDirection *dir = PortDirection::unknown();
|
||||||
type = LibertyPgPort::PgType::primary_ground;
|
switch (type) {
|
||||||
else if (stringEqual(type_name, "primary_power"))
|
case PwrGndType::primary_ground:;
|
||||||
type = LibertyPgPort::PgType::primary_power;
|
case PwrGndType::backup_ground:
|
||||||
|
case PwrGndType::internal_ground:
|
||||||
else if (stringEqual(type_name, "backup_ground"))
|
dir = PortDirection::ground();
|
||||||
type = LibertyPgPort::PgType::backup_ground;
|
break;
|
||||||
else if (stringEqual(type_name, "backup_power"))
|
case PwrGndType::primary_power:
|
||||||
type = LibertyPgPort::PgType::backup_power;
|
case PwrGndType::backup_power:
|
||||||
|
case PwrGndType::internal_power:
|
||||||
else if (stringEqual(type_name, "internal_ground"))
|
dir = PortDirection::power();
|
||||||
type = LibertyPgPort::PgType::internal_ground;
|
break;
|
||||||
else if (stringEqual(type_name, "internal_power"))
|
case PwrGndType::none:
|
||||||
type = LibertyPgPort::PgType::internal_power;
|
|
||||||
|
|
||||||
else if (stringEqual(type_name, "nwell"))
|
|
||||||
type = LibertyPgPort::PgType::nwell;
|
|
||||||
else if (stringEqual(type_name, "pwell"))
|
|
||||||
type = LibertyPgPort::PgType::pwell;
|
|
||||||
|
|
||||||
else if (stringEqual(type_name, "deepnwell"))
|
|
||||||
type = LibertyPgPort::PgType::deepnwell;
|
|
||||||
else if (stringEqual(type_name, "deeppwell"))
|
|
||||||
type = LibertyPgPort::PgType::deeppwell;
|
|
||||||
|
|
||||||
else
|
|
||||||
libError(1291, attr, "unknown pg_type.");
|
libError(1291, attr, "unknown pg_type.");
|
||||||
pg_port_->setPgType(type);
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pg_port_->setPwrGndType(type);
|
||||||
|
pg_port_->setDirection(dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -653,7 +653,7 @@ protected:
|
||||||
const EarlyLateAll *derate_type_;
|
const EarlyLateAll *derate_type_;
|
||||||
const EarlyLateAll *sigma_type_;
|
const EarlyLateAll *sigma_type_;
|
||||||
PathType path_type_;
|
PathType path_type_;
|
||||||
LibertyPgPort *pg_port_;
|
LibertyPort *pg_port_;
|
||||||
ScaleFactorType scale_factor_type_;
|
ScaleFactorType scale_factor_type_;
|
||||||
TableAxisPtr axis_[3];
|
TableAxisPtr axis_[3];
|
||||||
TablePtr table_;
|
TablePtr table_;
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ protected:
|
||||||
void writeCells();
|
void writeCells();
|
||||||
void writeCell(const LibertyCell *cell);
|
void writeCell(const LibertyCell *cell);
|
||||||
void writePort(const LibertyPort *port);
|
void writePort(const LibertyPort *port);
|
||||||
|
void writePwrGndPort(const LibertyPort *port);
|
||||||
void writeBusPort(const LibertyPort *port);
|
void writeBusPort(const LibertyPort *port);
|
||||||
void writePortAttrs(const LibertyPort *port);
|
void writePortAttrs(const LibertyPort *port);
|
||||||
void writeTimingArcSet(const TimingArcSet *arc_set);
|
void writeTimingArcSet(const TimingArcSet *arc_set);
|
||||||
|
|
@ -310,7 +311,9 @@ LibertyWriter::writeCell(const LibertyCell *cell)
|
||||||
while (port_iter.hasNext()) {
|
while (port_iter.hasNext()) {
|
||||||
const LibertyPort *port = port_iter.next();
|
const LibertyPort *port = port_iter.next();
|
||||||
if (!port->direction()->isInternal()) {
|
if (!port->direction()->isInternal()) {
|
||||||
if (port->isBus())
|
if (port->isPwrGnd())
|
||||||
|
writePwrGndPort(port);
|
||||||
|
else if (port->isBus())
|
||||||
writeBusPort(port);
|
writeBusPort(port);
|
||||||
else if (port->isBundle())
|
else if (port->isBundle())
|
||||||
report_->error(1340, "%s/%s bundled ports not supported.",
|
report_->error(1340, "%s/%s bundled ports not supported.",
|
||||||
|
|
@ -394,6 +397,15 @@ LibertyWriter::writePortAttrs(const LibertyPort *port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LibertyWriter::writePwrGndPort(const LibertyPort *port)
|
||||||
|
{
|
||||||
|
fprintf(stream_, " pg_pin(\"%s\") {\n", port->name());
|
||||||
|
fprintf(stream_, " pg_type : \"%s\";\n", pwrGndTypeName(port->pwrGndType()));
|
||||||
|
fprintf(stream_, " voltage_name : \"%s\";\n", port->voltageName());
|
||||||
|
fprintf(stream_, " }\n");
|
||||||
|
}
|
||||||
|
|
||||||
// Check if arc is added for port min_pulse_width_high/low attribute.
|
// Check if arc is added for port min_pulse_width_high/low attribute.
|
||||||
bool
|
bool
|
||||||
LibertyWriter::isAutoWidthArc(const LibertyPort *port,
|
LibertyWriter::isAutoWidthArc(const LibertyPort *port,
|
||||||
|
|
|
||||||
|
|
@ -753,6 +753,15 @@ bool is_top_level_port() { return Sta::sta()->ensureLinked()->isTopLevelPort(sel
|
||||||
PinConnectedPinIterator *connected_pin_iterator()
|
PinConnectedPinIterator *connected_pin_iterator()
|
||||||
{ return Sta::sta()->ensureLinked()->connectedPinIterator(self); }
|
{ return Sta::sta()->ensureLinked()->connectedPinIterator(self); }
|
||||||
|
|
||||||
|
bool
|
||||||
|
is_pwr_gnd()
|
||||||
|
{
|
||||||
|
sta::Sta *sta = Sta::sta();
|
||||||
|
Network *network = sta->network();
|
||||||
|
LibertyPort *lib_port = network->libertyPort(self);
|
||||||
|
return lib_port && lib_port->isPwrGnd();
|
||||||
|
}
|
||||||
|
|
||||||
Vertex **
|
Vertex **
|
||||||
vertices()
|
vertices()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,8 @@ Power::Power(StaState *sta) :
|
||||||
input_activity_(), // default set in ensureActivities()
|
input_activity_(), // default set in ensureActivities()
|
||||||
seq_activity_map_(100, SeqPinHash(network_), SeqPinEqual()),
|
seq_activity_map_(100, SeqPinHash(network_), SeqPinEqual()),
|
||||||
activities_valid_(false),
|
activities_valid_(false),
|
||||||
bdd_(sta)
|
bdd_(sta),
|
||||||
|
corner_(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,6 +114,8 @@ Power::clear()
|
||||||
seq_activity_map_.clear();
|
seq_activity_map_.clear();
|
||||||
activity_map_.clear();
|
activity_map_.clear();
|
||||||
activities_valid_ = false;
|
activities_valid_ = false;
|
||||||
|
instance_powers_.clear();
|
||||||
|
corner_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -286,13 +289,10 @@ Power::power(const Corner *corner,
|
||||||
pad.clear();
|
pad.clear();
|
||||||
|
|
||||||
ensureActivities();
|
ensureActivities();
|
||||||
Stats stats(debug_, report_);
|
ensureInstPowers(corner);
|
||||||
LeafInstanceIterator *inst_iter = network_->leafInstanceIterator();
|
for (auto [inst, inst_power] : instance_powers_) {
|
||||||
while (inst_iter->hasNext()) {
|
|
||||||
Instance *inst = inst_iter->next();
|
|
||||||
LibertyCell *cell = network_->libertyCell(inst);
|
LibertyCell *cell = network_->libertyCell(inst);
|
||||||
if (cell) {
|
if (cell) {
|
||||||
PowerResult inst_power = power(inst, cell, corner);
|
|
||||||
if (cell->isMacro()
|
if (cell->isMacro()
|
||||||
|| cell->isMemory()
|
|| cell->isMemory()
|
||||||
|| cell->interfaceTiming())
|
|| cell->interfaceTiming())
|
||||||
|
|
@ -308,8 +308,6 @@ Power::power(const Corner *corner,
|
||||||
total.incr(inst_power);
|
total.incr(inst_power);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete inst_iter;
|
|
||||||
stats.report("Find power");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -332,17 +330,15 @@ PowerResult
|
||||||
Power::power(const Instance *inst,
|
Power::power(const Instance *inst,
|
||||||
const Corner *corner)
|
const Corner *corner)
|
||||||
{
|
{
|
||||||
|
ensureActivities();
|
||||||
|
ensureInstPowers(corner);
|
||||||
if (network_->isHierarchical(inst)) {
|
if (network_->isHierarchical(inst)) {
|
||||||
PowerResult result;
|
PowerResult result;
|
||||||
powerInside(inst, corner, result);
|
powerInside(inst, corner, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
LibertyCell *cell = network_->libertyCell(inst);
|
else
|
||||||
if (cell) {
|
return instance_powers_[inst];
|
||||||
ensureActivities();
|
|
||||||
return power(inst, cell, corner);
|
|
||||||
}
|
|
||||||
return PowerResult();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -355,13 +351,8 @@ Power::powerInside(const Instance *hinst,
|
||||||
Instance *child = child_iter->next();
|
Instance *child = child_iter->next();
|
||||||
if (network_->isHierarchical(child))
|
if (network_->isHierarchical(child))
|
||||||
powerInside(child, corner, result);
|
powerInside(child, corner, result);
|
||||||
else {
|
else
|
||||||
LibertyCell *cell = network_->libertyCell(child);
|
result.incr(instance_powers_[child]);
|
||||||
if (cell) {
|
|
||||||
PowerResult inst_power = power(child, cell, corner);
|
|
||||||
result.incr(inst_power);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
delete child_iter;
|
delete child_iter;
|
||||||
}
|
}
|
||||||
|
|
@ -707,6 +698,7 @@ Power::evalBddActivity(DdNode *bdd,
|
||||||
void
|
void
|
||||||
Power::ensureActivities()
|
Power::ensureActivities()
|
||||||
{
|
{
|
||||||
|
Stats stats(debug_, report_);
|
||||||
// No need to propagate activites if global activity is set.
|
// No need to propagate activites if global activity is set.
|
||||||
if (!global_activity_.isSet()) {
|
if (!global_activity_.isSet()) {
|
||||||
if (!activities_valid_) {
|
if (!activities_valid_) {
|
||||||
|
|
@ -750,6 +742,7 @@ Power::ensureActivities()
|
||||||
activities_valid_ = true;
|
activities_valid_ = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stats.report("Power activities");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -841,6 +834,32 @@ Power::seedRegOutputActivities(const Instance *reg,
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void
|
||||||
|
Power::ensureInstPowers(const Corner *corner)
|
||||||
|
{
|
||||||
|
if (instance_powers_.empty()
|
||||||
|
|| corner != corner_)
|
||||||
|
findInstPowers(corner);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Power::findInstPowers(const Corner *corner)
|
||||||
|
{
|
||||||
|
Stats stats(debug_, report_);
|
||||||
|
LeafInstanceIterator *inst_iter = network_->leafInstanceIterator();
|
||||||
|
while (inst_iter->hasNext()) {
|
||||||
|
Instance *inst = inst_iter->next();
|
||||||
|
LibertyCell *cell = network_->libertyCell(inst);
|
||||||
|
if (cell) {
|
||||||
|
PowerResult inst_power = power(inst, cell, corner);
|
||||||
|
instance_powers_[inst] = inst_power;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete inst_iter;
|
||||||
|
corner_ = corner;
|
||||||
|
stats.report("Find power");
|
||||||
|
}
|
||||||
|
|
||||||
PowerResult
|
PowerResult
|
||||||
Power::power(const Instance *inst,
|
Power::power(const Instance *inst,
|
||||||
LibertyCell *cell,
|
LibertyCell *cell,
|
||||||
|
|
@ -1368,7 +1387,7 @@ Power::pgNameVoltage(LibertyCell *cell,
|
||||||
const DcalcAnalysisPt *dcalc_ap)
|
const DcalcAnalysisPt *dcalc_ap)
|
||||||
{
|
{
|
||||||
if (pg_port_name) {
|
if (pg_port_name) {
|
||||||
LibertyPgPort *pg_port = cell->findPgPort(pg_port_name);
|
LibertyPort *pg_port = cell->findLibertyPort(pg_port_name);
|
||||||
if (pg_port) {
|
if (pg_port) {
|
||||||
const char *volt_name = pg_port->voltageName();
|
const char *volt_name = pg_port->voltageName();
|
||||||
LibertyLibrary *library = cell->libertyLibrary();
|
LibertyLibrary *library = cell->libertyLibrary();
|
||||||
|
|
@ -1484,14 +1503,17 @@ Power::findUnannotatedPins(const Instance *inst,
|
||||||
InstancePinIterator *pin_iter = network_->pinIterator(inst);
|
InstancePinIterator *pin_iter = network_->pinIterator(inst);
|
||||||
while (pin_iter->hasNext()) {
|
while (pin_iter->hasNext()) {
|
||||||
const Pin *pin = pin_iter->next();
|
const Pin *pin = pin_iter->next();
|
||||||
|
LibertyPort *liberty_port = sdc_network_->libertyPort(pin);
|
||||||
if (!network_->direction(pin)->isInternal()
|
if (!network_->direction(pin)->isInternal()
|
||||||
|
&& !network_->direction(pin)->isPowerGround()
|
||||||
|
&& !(liberty_port && liberty_port->isPwrGnd())
|
||||||
&& user_activity_map_.find(pin) == user_activity_map_.end())
|
&& user_activity_map_.find(pin) == user_activity_map_.end())
|
||||||
unannotated_pins.push_back(pin);
|
unannotated_pins.push_back(pin);
|
||||||
}
|
}
|
||||||
delete pin_iter;
|
delete pin_iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
// leaf pins - internal pins + top instance pins
|
// leaf pins - internal pins - power/ground pins + top instance pins
|
||||||
size_t
|
size_t
|
||||||
Power::pinCount()
|
Power::pinCount()
|
||||||
{
|
{
|
||||||
|
|
@ -1502,7 +1524,10 @@ Power::pinCount()
|
||||||
InstancePinIterator *pin_iter = network_->pinIterator(leaf);
|
InstancePinIterator *pin_iter = network_->pinIterator(leaf);
|
||||||
while (pin_iter->hasNext()) {
|
while (pin_iter->hasNext()) {
|
||||||
const Pin *pin = pin_iter->next();
|
const Pin *pin = pin_iter->next();
|
||||||
if (!network_->direction(pin)->isInternal())
|
LibertyPort *liberty_port = sdc_network_->libertyPort(pin);
|
||||||
|
if (!network_->direction(pin)->isInternal()
|
||||||
|
&& !network_->direction(pin)->isPowerGround()
|
||||||
|
&& !(liberty_port && liberty_port->isPwrGnd()))
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
delete pin_iter;
|
delete pin_iter;
|
||||||
|
|
@ -1533,6 +1558,22 @@ Power::clockMinPeriod()
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Power::deleteInstanceBefore(const Instance *)
|
||||||
|
{
|
||||||
|
activities_valid_ = false;
|
||||||
|
instance_powers_.clear();
|
||||||
|
corner_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Power::deletePinBefore(const Pin *)
|
||||||
|
{
|
||||||
|
activities_valid_ = false;
|
||||||
|
instance_powers_.clear();
|
||||||
|
corner_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
PowerResult::PowerResult() :
|
PowerResult::PowerResult() :
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,8 @@ public:
|
||||||
float clockMinPeriod();
|
float clockMinPeriod();
|
||||||
InstanceSeq highestPowerInstances(size_t count,
|
InstanceSeq highestPowerInstances(size_t count,
|
||||||
const Corner *corner);
|
const Corner *corner);
|
||||||
|
void deleteInstanceBefore(const Instance *inst);
|
||||||
|
void deletePinBefore(const Pin *pin);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PwrActivity &activity(const Pin *pin);
|
PwrActivity &activity(const Pin *pin);
|
||||||
|
|
@ -129,6 +131,8 @@ protected:
|
||||||
PwrActivity &activity);
|
PwrActivity &activity);
|
||||||
PwrActivity findActivity(const Pin *pin);
|
PwrActivity findActivity(const Pin *pin);
|
||||||
|
|
||||||
|
void ensureInstPowers(const Corner *corner);
|
||||||
|
void findInstPowers(const Corner *corner);
|
||||||
PowerResult power(const Instance *inst,
|
PowerResult power(const Instance *inst,
|
||||||
LibertyCell *cell,
|
LibertyCell *cell,
|
||||||
const Corner *corner);
|
const Corner *corner);
|
||||||
|
|
@ -229,6 +233,8 @@ private:
|
||||||
PwrSeqActivityMap seq_activity_map_;
|
PwrSeqActivityMap seq_activity_map_;
|
||||||
bool activities_valid_;
|
bool activities_valid_;
|
||||||
Bdd bdd_;
|
Bdd bdd_;
|
||||||
|
std::map<const Instance*, PowerResult> instance_powers_;
|
||||||
|
const Corner *corner_;
|
||||||
|
|
||||||
static constexpr int max_activity_passes_ = 100;
|
static constexpr int max_activity_passes_ = 100;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
#include "Report.hh"
|
#include "Report.hh"
|
||||||
#include "Network.hh"
|
#include "Network.hh"
|
||||||
#include "PortDirection.hh"
|
#include "PortDirection.hh"
|
||||||
|
#include "Liberty.hh"
|
||||||
#include "Sdc.hh"
|
#include "Sdc.hh"
|
||||||
#include "Power.hh"
|
#include "Power.hh"
|
||||||
#include "power/SaifReaderPvt.hh"
|
#include "power/SaifReaderPvt.hh"
|
||||||
|
|
@ -168,9 +169,11 @@ SaifReader::setNetDurations(const char *net_name,
|
||||||
if (parent) {
|
if (parent) {
|
||||||
string unescaped_name = unescaped(net_name);
|
string unescaped_name = unescaped(net_name);
|
||||||
const Pin *pin = sdc_network_->findPin(parent, unescaped_name.c_str());
|
const Pin *pin = sdc_network_->findPin(parent, unescaped_name.c_str());
|
||||||
|
LibertyPort *liberty_port = pin ? sdc_network_->libertyPort(pin) : nullptr;
|
||||||
if (pin
|
if (pin
|
||||||
&& !sdc_network_->isHierarchical(pin)
|
&& !sdc_network_->isHierarchical(pin)
|
||||||
&& !sdc_network_->direction(pin)->isInternal()) {
|
&& !sdc_network_->direction(pin)->isInternal()
|
||||||
|
&& !(liberty_port && liberty_port->isPwrGnd())) {
|
||||||
double t1 = durations[static_cast<int>(SaifState::T1)];
|
double t1 = durations[static_cast<int>(SaifState::T1)];
|
||||||
float duty = t1 / duration_;
|
float duty = t1 / duration_;
|
||||||
double tc = durations[static_cast<int>(SaifState::TC)];
|
double tc = durations[static_cast<int>(SaifState::TC)];
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
#include "VcdParse.hh"
|
#include "VcdParse.hh"
|
||||||
#include "Debug.hh"
|
#include "Debug.hh"
|
||||||
#include "Network.hh"
|
#include "Network.hh"
|
||||||
|
#include "Liberty.hh"
|
||||||
#include "PortDirection.hh"
|
#include "PortDirection.hh"
|
||||||
#include "VerilogNamespace.hh"
|
#include "VerilogNamespace.hh"
|
||||||
#include "ParseBus.hh"
|
#include "ParseBus.hh"
|
||||||
|
|
@ -284,13 +285,16 @@ VcdCountReader::addVarPin(const string &pin_name,
|
||||||
size_t bit_idx)
|
size_t bit_idx)
|
||||||
{
|
{
|
||||||
const Pin *pin = sdc_network_->findPin(pin_name.c_str());
|
const Pin *pin = sdc_network_->findPin(pin_name.c_str());
|
||||||
|
LibertyPort *liberty_port = pin ? sdc_network_->libertyPort(pin) : nullptr;
|
||||||
if (pin
|
if (pin
|
||||||
&& !sdc_network_->isHierarchical(pin)
|
&& !sdc_network_->isHierarchical(pin)
|
||||||
&& !sdc_network_->direction(pin)->isInternal()) {
|
&& !sdc_network_->direction(pin)->isInternal()
|
||||||
|
&& !sdc_network_->direction(pin)->isPowerGround()
|
||||||
|
&& !(liberty_port && liberty_port->isPwrGnd())) {
|
||||||
VcdCounts &vcd_counts = vcd_count_map_[id];
|
VcdCounts &vcd_counts = vcd_count_map_[id];
|
||||||
vcd_counts.resize(width);
|
vcd_counts.resize(width);
|
||||||
vcd_counts[bit_idx].addPin(pin);
|
vcd_counts[bit_idx].addPin(pin);
|
||||||
debugPrint(debug_, "read_vcd_activities", 2, "id %s pin %s",
|
debugPrint(debug_, "read_vcd", 2, "id %s pin %s",
|
||||||
id.c_str(),
|
id.c_str(),
|
||||||
pin_name.c_str());
|
pin_name.c_str());
|
||||||
}
|
}
|
||||||
|
|
@ -304,11 +308,11 @@ VcdCountReader::varAppendValue(const string &id,
|
||||||
const auto &itr = vcd_count_map_.find(id);
|
const auto &itr = vcd_count_map_.find(id);
|
||||||
if (itr != vcd_count_map_.end()) {
|
if (itr != vcd_count_map_.end()) {
|
||||||
VcdCounts &vcd_counts = itr->second;
|
VcdCounts &vcd_counts = itr->second;
|
||||||
if (debug_->check("read_vcd_activities", 3)) {
|
if (debug_->check("read_vcd", 3)) {
|
||||||
for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) {
|
for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) {
|
||||||
VcdCount &vcd_count = vcd_counts[bit_idx];
|
VcdCount &vcd_count = vcd_counts[bit_idx];
|
||||||
for (const Pin *pin : vcd_count.pins()) {
|
for (const Pin *pin : vcd_count.pins()) {
|
||||||
debugPrint(debug_, "read_vcd_activities", 3, "%s time %" PRIu64 " value %c",
|
debugPrint(debug_, "read_vcd", 3, "%s time %" PRIu64 " value %c",
|
||||||
sdc_network_->pathName(pin),
|
sdc_network_->pathName(pin),
|
||||||
time,
|
time,
|
||||||
value);
|
value);
|
||||||
|
|
@ -333,9 +337,9 @@ VcdCountReader::varAppendBusValue(const string &id,
|
||||||
for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) {
|
for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) {
|
||||||
char bit_value = ((bus_value >> bit_idx) & 0x1) ? '1' : '0';
|
char bit_value = ((bus_value >> bit_idx) & 0x1) ? '1' : '0';
|
||||||
VcdCount &vcd_count = vcd_counts[bit_idx];
|
VcdCount &vcd_count = vcd_counts[bit_idx];
|
||||||
if (debug_->check("read_vcd_activities", 3)) {
|
if (debug_->check("read_vcd", 3)) {
|
||||||
for (const Pin *pin : vcd_count.pins()) {
|
for (const Pin *pin : vcd_count.pins()) {
|
||||||
debugPrint(debug_, "read_vcd_activities", 3, "%s time %" PRIu64 " value %c",
|
debugPrint(debug_, "read_vcd", 3, "%s time %" PRIu64 " value %c",
|
||||||
sdc_network_->pathName(pin),
|
sdc_network_->pathName(pin),
|
||||||
time,
|
time,
|
||||||
bit_value);
|
bit_value);
|
||||||
|
|
@ -420,9 +424,9 @@ ReadVcdActivities::setActivities()
|
||||||
VcdTime high_time = vcd_count.highTime(time_max);
|
VcdTime high_time = vcd_count.highTime(time_max);
|
||||||
float duty = static_cast<double>(high_time) / time_delta;
|
float duty = static_cast<double>(high_time) / time_delta;
|
||||||
float density = transition_count / (time_delta * time_scale);
|
float density = transition_count / (time_delta * time_scale);
|
||||||
if (debug_->check("read_vcd_activities", 1)) {
|
if (debug_->check("read_vcd", 1)) {
|
||||||
for (const Pin *pin : vcd_count.pins()) {
|
for (const Pin *pin : vcd_count.pins()) {
|
||||||
debugPrint(debug_, "read_vcd_activities", 1,
|
debugPrint(debug_, "read_vcd", 1,
|
||||||
"%s transitions %.1f activity %.2f duty %.2f",
|
"%s transitions %.1f activity %.2f duty %.2f",
|
||||||
sdc_network_->pathName(pin),
|
sdc_network_->pathName(pin),
|
||||||
transition_count,
|
transition_count,
|
||||||
|
|
|
||||||
397
sdc/Sdc.cc
397
sdc/Sdc.cc
|
|
@ -3957,13 +3957,29 @@ Sdc::unrecordPathDelayInternalFrom(ExceptionPath *exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class OBJ>
|
||||||
|
const ExceptionPathSet *
|
||||||
|
findExceptions(const UnorderedMap<const OBJ*, ExceptionPathSet> &map,
|
||||||
|
const OBJ *obj)
|
||||||
|
{
|
||||||
|
const auto itr = map.find(obj);
|
||||||
|
if (itr != map.end())
|
||||||
|
return &itr->second;
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Sdc::pathDelayFrom(const Pin *pin)
|
Sdc::pathDelayFrom(const Pin *pin)
|
||||||
{
|
{
|
||||||
ExceptionPathSet *exceptions = first_from_pin_exceptions_.findKey(pin);
|
|
||||||
for (ExceptionPath *exception : *exceptions) {
|
const ExceptionPathSet *exceptions =
|
||||||
if (exception->isPathDelay())
|
findExceptions<Pin>(first_from_pin_exceptions_, pin);
|
||||||
return true;
|
if (exceptions) {
|
||||||
|
for (ExceptionPath *exception : *exceptions) {
|
||||||
|
if (exception->isPathDelay())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -4042,10 +4058,13 @@ Sdc::hasLibertyCheckTo(const Pin *pin)
|
||||||
bool
|
bool
|
||||||
Sdc::pathDelayTo(const Pin *pin)
|
Sdc::pathDelayTo(const Pin *pin)
|
||||||
{
|
{
|
||||||
ExceptionPathSet *exceptions = first_to_pin_exceptions_.findKey(pin);
|
const ExceptionPathSet *exceptions =
|
||||||
for (ExceptionPath *exception : *exceptions) {
|
findExceptions<Pin>(first_to_pin_exceptions_, pin);
|
||||||
if (exception->isPathDelay())
|
if (exceptions) {
|
||||||
return true;
|
for (ExceptionPath *exception : *exceptions) {
|
||||||
|
if (exception->isPathDelay())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -4210,11 +4229,14 @@ void
|
||||||
Sdc::deleteLoopExceptions()
|
Sdc::deleteLoopExceptions()
|
||||||
{
|
{
|
||||||
// erase prevents range iteration.
|
// erase prevents range iteration.
|
||||||
ExceptionPathSet::Iterator except_iter(exceptions_);
|
for (auto itr = exceptions_.begin(); itr != exceptions_.end(); ) {
|
||||||
while (except_iter.hasNext()) {
|
ExceptionPath *except = *itr;
|
||||||
ExceptionPath *except = except_iter.next();
|
if (except->isLoop()) {
|
||||||
if (except->isLoop())
|
itr = exceptions_.erase(itr);
|
||||||
deleteException(except);
|
deleteException(except);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
itr++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4420,10 +4442,10 @@ Sdc::findMatchingExceptionsFirstThru(ExceptionPath *exception,
|
||||||
for (const Net *net : *thru->nets()) {
|
for (const Net *net : *thru->nets()) {
|
||||||
// Potential matches includes exceptions that match net that are not
|
// Potential matches includes exceptions that match net that are not
|
||||||
// the first exception point.
|
// the first exception point.
|
||||||
ExceptionPathSet *potential_matches =
|
const ExceptionPathSet *potential_matches =
|
||||||
first_thru_net_exceptions_.findKey(net);
|
findExceptions<Net>(first_thru_net_exceptions_, net);
|
||||||
if (potential_matches) {
|
if (potential_matches) {
|
||||||
for (ExceptionPath *match : *potential_matches) {
|
for (ExceptionPath *match : *potential_matches) {
|
||||||
ExceptionThru *match_thru = (*match->thrus())[0];
|
ExceptionThru *match_thru = (*match->thrus())[0];
|
||||||
if (match_thru->nets()->hasKey(net)
|
if (match_thru->nets()->hasKey(net)
|
||||||
&& match->overrides(exception)
|
&& match->overrides(exception)
|
||||||
|
|
@ -4457,8 +4479,11 @@ Sdc::findMatchingExceptionsClks(ExceptionPath *exception,
|
||||||
{
|
{
|
||||||
if (clks) {
|
if (clks) {
|
||||||
ExceptionPathSet clks_matches;
|
ExceptionPathSet clks_matches;
|
||||||
for (Clock *clk : *clks)
|
for (Clock *clk : *clks) {
|
||||||
clks_matches.insertSet(exception_map.findKey(clk));
|
auto itr = exception_map.find(clk);
|
||||||
|
if (itr != exception_map.end())
|
||||||
|
clks_matches.insert(itr->second.begin(), itr->second.end());
|
||||||
|
}
|
||||||
findMatchingExceptions(exception, &clks_matches, matches);
|
findMatchingExceptions(exception, &clks_matches, matches);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4471,8 +4496,11 @@ Sdc::findMatchingExceptionsPins(ExceptionPath *exception,
|
||||||
{
|
{
|
||||||
if (pins) {
|
if (pins) {
|
||||||
ExceptionPathSet pins_matches;
|
ExceptionPathSet pins_matches;
|
||||||
for (const Pin *pin : *pins)
|
for (const Pin *pin : *pins) {
|
||||||
pins_matches.insertSet(exception_map.findKey(pin));
|
auto itr = exception_map.find(pin);
|
||||||
|
if (itr != exception_map.end())
|
||||||
|
pins_matches.insert(itr->second.begin(), itr->second.end());
|
||||||
|
}
|
||||||
findMatchingExceptions(exception, &pins_matches, matches);
|
findMatchingExceptions(exception, &pins_matches, matches);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4484,10 +4512,13 @@ Sdc::findMatchingExceptionsInsts(ExceptionPath *exception,
|
||||||
ExceptionPathSet &matches)
|
ExceptionPathSet &matches)
|
||||||
{
|
{
|
||||||
if (insts) {
|
if (insts) {
|
||||||
ExceptionPathSet insts_matches;
|
ExceptionPathSet inst_matches;
|
||||||
for (const Instance *inst : *insts)
|
for (const Instance *inst : *insts) {
|
||||||
insts_matches.insertSet(exception_map.findKey(inst));
|
auto itr = exception_map.find(inst);
|
||||||
findMatchingExceptions(exception, &insts_matches, matches);
|
if (itr != exception_map.end())
|
||||||
|
inst_matches.insert(itr->second.begin(), itr->second.end());
|
||||||
|
}
|
||||||
|
findMatchingExceptions(exception, &inst_matches, matches);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4639,12 +4670,8 @@ Sdc::recordMergeHash(ExceptionPath *exception,
|
||||||
hash,
|
hash,
|
||||||
exception->asString(network_),
|
exception->asString(network_),
|
||||||
missing_pt->asString(network_));
|
missing_pt->asString(network_));
|
||||||
ExceptionPathSet *set = exception_merge_hash_.findKey(hash);
|
ExceptionPathSet &set = exception_merge_hash_[hash];
|
||||||
if (set == nullptr) {
|
set.insert(exception);
|
||||||
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.
|
// Record a mapping from first pin/clock/instance's to a set of exceptions.
|
||||||
|
|
@ -4716,12 +4743,8 @@ Sdc::recordExceptionClks(ExceptionPath *exception,
|
||||||
{
|
{
|
||||||
if (clks) {
|
if (clks) {
|
||||||
for (Clock *clk : *clks) {
|
for (Clock *clk : *clks) {
|
||||||
ExceptionPathSet *set = exception_map.findKey(clk);
|
ExceptionPathSet &set = exception_map[clk];
|
||||||
if (set == nullptr) {
|
set.insert(exception);
|
||||||
set = new ExceptionPathSet;
|
|
||||||
exception_map[clk] = set;
|
|
||||||
}
|
|
||||||
set->insert(exception);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4733,12 +4756,8 @@ Sdc::recordExceptionEdges(ExceptionPath *exception,
|
||||||
{
|
{
|
||||||
if (edges) {
|
if (edges) {
|
||||||
for (const EdgePins &edge : *edges) {
|
for (const EdgePins &edge : *edges) {
|
||||||
ExceptionPathSet *set = exception_map.findKey(edge);
|
ExceptionPathSet &set = exception_map[edge];
|
||||||
if (set == nullptr) {
|
set.insert(exception);
|
||||||
set = new ExceptionPathSet;
|
|
||||||
exception_map.insert(edge, set);
|
|
||||||
}
|
|
||||||
set->insert(exception);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4750,12 +4769,8 @@ Sdc::recordExceptionPins(ExceptionPath *exception,
|
||||||
{
|
{
|
||||||
if (pins) {
|
if (pins) {
|
||||||
for (const Pin *pin : *pins) {
|
for (const Pin *pin : *pins) {
|
||||||
ExceptionPathSet *set = exception_map.findKey(pin);
|
ExceptionPathSet &set = exception_map[pin];
|
||||||
if (set == nullptr) {
|
set.insert(exception);
|
||||||
set = new ExceptionPathSet;
|
|
||||||
exception_map.insert(pin, set);
|
|
||||||
}
|
|
||||||
set->insert(exception);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4765,12 +4780,8 @@ Sdc::recordExceptionHpin(ExceptionPath *exception,
|
||||||
Pin *pin,
|
Pin *pin,
|
||||||
PinExceptionsMap &exception_map)
|
PinExceptionsMap &exception_map)
|
||||||
{
|
{
|
||||||
ExceptionPathSet *set = exception_map.findKey(pin);
|
ExceptionPathSet &set = exception_map[pin];
|
||||||
if (set == nullptr) {
|
set.insert(exception);
|
||||||
set = new ExceptionPathSet;
|
|
||||||
exception_map.insert(pin, set);
|
|
||||||
}
|
|
||||||
set->insert(exception);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -4780,12 +4791,8 @@ Sdc::recordExceptionInsts(ExceptionPath *exception,
|
||||||
{
|
{
|
||||||
if (insts) {
|
if (insts) {
|
||||||
for (const Instance *inst : *insts) {
|
for (const Instance *inst : *insts) {
|
||||||
ExceptionPathSet *set = exception_map.findKey(inst);
|
ExceptionPathSet &set = exception_map[inst];
|
||||||
if (set == nullptr) {
|
set.insert(exception);
|
||||||
set = new ExceptionPathSet;
|
|
||||||
exception_map[inst] = set;
|
|
||||||
}
|
|
||||||
set->insert(exception);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4797,12 +4804,8 @@ Sdc::recordExceptionNets(ExceptionPath *exception,
|
||||||
{
|
{
|
||||||
if (nets) {
|
if (nets) {
|
||||||
for (const Net *net : *nets) {
|
for (const Net *net : *nets) {
|
||||||
ExceptionPathSet *set = exception_map.findKey(net);
|
ExceptionPathSet &set = exception_map[net];
|
||||||
if (set == nullptr) {
|
set.insert(exception);
|
||||||
set = new ExceptionPathSet;
|
|
||||||
exception_map[net] = set;
|
|
||||||
}
|
|
||||||
set->insert(exception);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4836,9 +4839,10 @@ Sdc::findMergeMatch(ExceptionPath *exception)
|
||||||
while (missing_pt_iter.hasNext()) {
|
while (missing_pt_iter.hasNext()) {
|
||||||
ExceptionPt *missing_pt = missing_pt_iter.next();
|
ExceptionPt *missing_pt = missing_pt_iter.next();
|
||||||
size_t hash = exception->hash(missing_pt);
|
size_t hash = exception->hash(missing_pt);
|
||||||
ExceptionPathSet *matches = exception_merge_hash_.findKey(hash);
|
auto itr = exception_merge_hash_.find(hash);
|
||||||
if (matches) {
|
if (itr != exception_merge_hash_.end()) {
|
||||||
for (ExceptionPath *match : *matches) {
|
ExceptionPathSet &matches = itr->second;
|
||||||
|
for (ExceptionPath *match : matches) {
|
||||||
ExceptionPt *match_missing_pt;
|
ExceptionPt *match_missing_pt;
|
||||||
if (match != exception
|
if (match != exception
|
||||||
// Exceptions are not merged if their priorities are
|
// Exceptions are not merged if their priorities are
|
||||||
|
|
@ -4876,59 +4880,52 @@ Sdc::findMergeMatch(ExceptionPath *exception)
|
||||||
void
|
void
|
||||||
Sdc::deleteExceptions()
|
Sdc::deleteExceptions()
|
||||||
{
|
{
|
||||||
exceptions_.deleteContentsClear();
|
exceptions_.clear();
|
||||||
exception_id_ = 0;
|
exception_id_ = 0;
|
||||||
|
|
||||||
first_from_pin_exceptions_.deleteContentsClear();
|
first_from_pin_exceptions_.clear();
|
||||||
first_from_clk_exceptions_.deleteContentsClear();
|
first_from_clk_exceptions_.clear();
|
||||||
first_from_inst_exceptions_.deleteContentsClear();
|
first_from_inst_exceptions_.clear();
|
||||||
first_to_pin_exceptions_.deleteContentsClear();
|
first_to_pin_exceptions_.clear();
|
||||||
first_to_clk_exceptions_.deleteContentsClear();
|
first_to_clk_exceptions_.clear();
|
||||||
first_to_inst_exceptions_.deleteContentsClear();
|
first_to_inst_exceptions_.clear();
|
||||||
first_thru_pin_exceptions_.deleteContentsClear();
|
first_thru_pin_exceptions_.clear();
|
||||||
first_thru_inst_exceptions_.deleteContentsClear();
|
first_thru_inst_exceptions_.clear();
|
||||||
first_thru_net_exceptions_.deleteContentsClear();
|
first_thru_net_exceptions_.clear();
|
||||||
first_thru_edge_exceptions_.deleteContentsClear();
|
first_thru_edge_exceptions_.clear();
|
||||||
first_thru_edge_exceptions_.clear();
|
first_thru_edge_exceptions_.clear();
|
||||||
path_delay_internal_from_.clear();
|
path_delay_internal_from_.clear();
|
||||||
path_delay_internal_from_break_.clear();
|
path_delay_internal_from_break_.clear();
|
||||||
path_delay_internal_to_.clear();
|
path_delay_internal_to_.clear();
|
||||||
path_delay_internal_to_break_.clear();
|
path_delay_internal_to_break_.clear();
|
||||||
pin_exceptions_.deleteContentsClear();
|
pin_exceptions_.clear();
|
||||||
|
|
||||||
deleteExceptionPtHashMapSets(exception_merge_hash_);
|
exception_merge_hash_.clear();
|
||||||
exception_merge_hash_.clear();
|
exception_merge_hash_.clear();
|
||||||
have_thru_hpin_exceptions_ = false;
|
have_thru_hpin_exceptions_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
Sdc::deleteExceptionPtHashMapSets(ExceptionPathPtHash &map)
|
|
||||||
{
|
|
||||||
map.deleteContents();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void
|
void
|
||||||
Sdc::deleteExceptionsReferencing(Clock *clk)
|
Sdc::deleteExceptionsReferencing(Clock *clk)
|
||||||
{
|
{
|
||||||
// erase prevents range iteration.
|
// erase prevents range iteration.
|
||||||
ExceptionPathSet::ConstIterator exception_iter(exceptions_);
|
for (auto itr = exceptions_.begin(); itr != exceptions_.end(); ) {
|
||||||
while (exception_iter.hasNext()) {
|
ExceptionPath *exception = *itr;
|
||||||
ExceptionPath *exception = exception_iter.next();
|
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
ExceptionFrom *from = exception->from();
|
ExceptionFrom *from = exception->from();
|
||||||
if (from) {
|
if (from) {
|
||||||
ClockSet *clks = from->clks();
|
ClockSet *clks = from->clks();
|
||||||
if (clks && clks->hasKey(clk)) {
|
if (clks && clks->hasKey(clk)) {
|
||||||
|
itr = exceptions_.erase(itr);
|
||||||
unrecordException(exception);
|
unrecordException(exception);
|
||||||
|
deleted = true;
|
||||||
from->deleteClock(clk);
|
from->deleteClock(clk);
|
||||||
if (from->hasObjects())
|
if (from->hasObjects())
|
||||||
recordException(exception);
|
recordException(exception);
|
||||||
else {
|
else
|
||||||
deleteException(exception);
|
deleteException(exception);
|
||||||
deleted = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4937,6 +4934,8 @@ Sdc::deleteExceptionsReferencing(Clock *clk)
|
||||||
if (to) {
|
if (to) {
|
||||||
ClockSet *clks = to->clks();
|
ClockSet *clks = to->clks();
|
||||||
if (clks && clks->hasKey(clk)) {
|
if (clks && clks->hasKey(clk)) {
|
||||||
|
itr = exceptions_.erase(itr);
|
||||||
|
deleted = true;
|
||||||
unrecordException(exception);
|
unrecordException(exception);
|
||||||
to->deleteClock(clk);
|
to->deleteClock(clk);
|
||||||
if (to->hasObjects())
|
if (to->hasObjects())
|
||||||
|
|
@ -4946,6 +4945,8 @@ Sdc::deleteExceptionsReferencing(Clock *clk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!deleted)
|
||||||
|
itr++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4986,9 +4987,11 @@ Sdc::unrecordMergeHash(ExceptionPath *exception,
|
||||||
hash,
|
hash,
|
||||||
exception->asString(network_),
|
exception->asString(network_),
|
||||||
missing_pt->asString(network_));
|
missing_pt->asString(network_));
|
||||||
ExceptionPathSet *matches = exception_merge_hash_.findKey(hash);
|
auto itr = exception_merge_hash_.find(hash);
|
||||||
if (matches)
|
if (itr != exception_merge_hash_.end()) {
|
||||||
matches->erase(exception);
|
ExceptionPathSet &matches = itr->second;
|
||||||
|
matches.erase(exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -5026,9 +5029,11 @@ Sdc::unrecordExceptionClks(ExceptionPath *exception,
|
||||||
{
|
{
|
||||||
if (clks) {
|
if (clks) {
|
||||||
for (Clock *clk : *clks) {
|
for (Clock *clk : *clks) {
|
||||||
ExceptionPathSet *set = exception_map.findKey(clk);
|
auto itr = exception_map.find(clk);
|
||||||
if (set)
|
if (itr != exception_map.end()) {
|
||||||
set->erase(exception);
|
ExceptionPathSet &set = itr->second;
|
||||||
|
set.erase(exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5040,9 +5045,11 @@ Sdc::unrecordExceptionPins(ExceptionPath *exception,
|
||||||
{
|
{
|
||||||
if (pins) {
|
if (pins) {
|
||||||
for (const Pin *pin : *pins) {
|
for (const Pin *pin : *pins) {
|
||||||
ExceptionPathSet *set = exception_map.findKey(pin);
|
auto itr = exception_map.find(pin);
|
||||||
if (set)
|
if (itr != exception_map.end()) {
|
||||||
set->erase(exception);
|
ExceptionPathSet &set = itr->second;
|
||||||
|
set.erase(exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5054,9 +5061,11 @@ Sdc::unrecordExceptionInsts(ExceptionPath *exception,
|
||||||
{
|
{
|
||||||
if (insts) {
|
if (insts) {
|
||||||
for (const Instance *inst : *insts) {
|
for (const Instance *inst : *insts) {
|
||||||
ExceptionPathSet *set = exception_map.findKey(inst);
|
auto itr = exception_map.find(inst);
|
||||||
if (set)
|
if (itr != exception_map.end()) {
|
||||||
set->erase(exception);
|
ExceptionPathSet &set = itr->second;
|
||||||
|
set.erase(exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5068,9 +5077,11 @@ Sdc::unrecordExceptionEdges(ExceptionPath *exception,
|
||||||
{
|
{
|
||||||
if (edges) {
|
if (edges) {
|
||||||
for (const EdgePins &edge : *edges) {
|
for (const EdgePins &edge : *edges) {
|
||||||
ExceptionPathSet *set = exception_map.findKey(edge);
|
auto itr = exception_map.find(edge);
|
||||||
if (set)
|
if (itr != exception_map.end()) {
|
||||||
set->erase(exception);
|
ExceptionPathSet &set = itr->second;
|
||||||
|
set.erase(exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5082,9 +5093,11 @@ Sdc::unrecordExceptionNets(ExceptionPath *exception,
|
||||||
{
|
{
|
||||||
if (nets) {
|
if (nets) {
|
||||||
for (const Net *net : *nets) {
|
for (const Net *net : *nets) {
|
||||||
ExceptionPathSet *set = exception_map.findKey(net);
|
auto itr = exception_map.find(net);
|
||||||
if (set)
|
if (itr != exception_map.end()) {
|
||||||
set->erase(exception);
|
ExceptionPathSet &set = itr->second;
|
||||||
|
set.erase(exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5094,9 +5107,11 @@ Sdc::unrecordExceptionHpin(ExceptionPath *exception,
|
||||||
Pin *pin,
|
Pin *pin,
|
||||||
PinExceptionsMap &exception_map)
|
PinExceptionsMap &exception_map)
|
||||||
{
|
{
|
||||||
ExceptionPathSet *set = exception_map.findKey(pin);
|
auto itr = exception_map.find(pin);
|
||||||
if (set)
|
if (itr != exception_map.end()) {
|
||||||
set->erase(exception);
|
ExceptionPathSet &set = itr->second;
|
||||||
|
set.erase(exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
@ -5166,18 +5181,18 @@ Sdc::resetPath(ExceptionFrom *from,
|
||||||
const MinMaxAll *min_max)
|
const MinMaxAll *min_max)
|
||||||
{
|
{
|
||||||
checkFromThrusTo(from, thrus, to);
|
checkFromThrusTo(from, thrus, to);
|
||||||
ExceptionPathSet::Iterator except_iter(exceptions_);
|
// erase prevents range iteration.
|
||||||
while (except_iter.hasNext()) {
|
for (auto itr = exceptions_.begin(); itr != exceptions_.end(); ) {
|
||||||
ExceptionPath *match = except_iter.next();
|
ExceptionPath *match = *itr;
|
||||||
if (match->resetMatch(from, thrus, to, min_max, network_)) {
|
if (match->resetMatch(from, thrus, to, min_max, network_)) {
|
||||||
debugPrint(debug_, "exception_match", 3, "reset match %s",
|
debugPrint(debug_, "exception_match", 3, "reset match %s",
|
||||||
match->asString(network_));
|
match->asString(network_));
|
||||||
ExceptionPathSet expansions;
|
ExceptionPathSet expansions;
|
||||||
expandException(match, expansions);
|
expandException(match, expansions);
|
||||||
|
itr = exceptions_.erase(itr);
|
||||||
deleteException(match);
|
deleteException(match);
|
||||||
ExceptionPathSet::Iterator expand_iter(expansions);
|
|
||||||
while (expand_iter.hasNext()) {
|
for (ExceptionPath *expand : expansions) {
|
||||||
ExceptionPath *expand = expand_iter.next();
|
|
||||||
if (expand->resetMatch(from, thrus, to, min_max, network_)) {
|
if (expand->resetMatch(from, thrus, to, min_max, network_)) {
|
||||||
unrecordPathDelayInternalFrom(expand);
|
unrecordPathDelayInternalFrom(expand);
|
||||||
unrecordPathDelayInternalTo(expand);
|
unrecordPathDelayInternalTo(expand);
|
||||||
|
|
@ -5187,6 +5202,8 @@ Sdc::resetPath(ExceptionFrom *from,
|
||||||
addException(expand);
|
addException(expand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
itr++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5214,33 +5231,38 @@ Sdc::exceptionFromStates(const Pin *pin,
|
||||||
{
|
{
|
||||||
bool srch_from = true;
|
bool srch_from = true;
|
||||||
if (pin) {
|
if (pin) {
|
||||||
if (srch_from && !first_from_pin_exceptions_.empty())
|
if (srch_from) {
|
||||||
srch_from &= exceptionFromStates(first_from_pin_exceptions_.findKey(pin),
|
const ExceptionPathSet *exceptions =
|
||||||
pin, rf, min_max, include_filter,
|
findExceptions<Pin>(first_from_pin_exceptions_, pin);
|
||||||
states);
|
srch_from &= exceptionFromStates(exceptions, pin, rf, min_max,
|
||||||
if (srch_from && !first_thru_pin_exceptions_.empty())
|
include_filter, states);
|
||||||
srch_from &= exceptionFromStates(first_thru_pin_exceptions_.findKey(pin),
|
}
|
||||||
pin, rf, min_max, include_filter,
|
if (srch_from) {
|
||||||
states);
|
const ExceptionPathSet *exceptions =
|
||||||
|
findExceptions<Pin>(first_thru_pin_exceptions_, pin);
|
||||||
|
srch_from &= exceptionFromStates(exceptions, pin, rf, min_max,
|
||||||
|
include_filter, states);
|
||||||
|
}
|
||||||
if (srch_from
|
if (srch_from
|
||||||
&& (!first_from_inst_exceptions_.empty()
|
&& (!first_from_inst_exceptions_.empty()
|
||||||
|| !first_thru_inst_exceptions_.empty())) {
|
|| !first_thru_inst_exceptions_.empty())) {
|
||||||
Instance *inst = network_->instance(pin);
|
Instance *inst = network_->instance(pin);
|
||||||
if (srch_from && !first_from_inst_exceptions_.empty())
|
const ExceptionPathSet *exceptions =
|
||||||
srch_from &= exceptionFromStates(first_from_inst_exceptions_.findKey(inst),
|
findExceptions<Instance>(first_from_inst_exceptions_, inst);
|
||||||
pin, rf, min_max, include_filter,
|
srch_from &= exceptionFromStates(exceptions, pin, rf, min_max,
|
||||||
states);
|
include_filter, states);
|
||||||
if (srch_from && !first_thru_inst_exceptions_.empty())
|
const ExceptionPathSet *exceptions2 =
|
||||||
srch_from &= exceptionFromStates(first_thru_inst_exceptions_.findKey(inst),
|
findExceptions<Instance>(first_thru_inst_exceptions_, inst);
|
||||||
pin, rf, min_max, include_filter,
|
srch_from &= exceptionFromStates(exceptions2, pin, rf, min_max,
|
||||||
states);
|
include_filter, states);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (srch_from && clk && !first_from_clk_exceptions_.empty())
|
if (srch_from && clk) {
|
||||||
srch_from &= exceptionFromStates(first_from_clk_exceptions_.findKey(clk),
|
const ExceptionPathSet *exceptions =
|
||||||
pin, clk_rf, min_max, include_filter,
|
findExceptions<Clock>(first_from_clk_exceptions_, clk);
|
||||||
states);
|
srch_from &= exceptionFromStates(exceptions, pin, clk_rf, min_max,
|
||||||
|
include_filter, states);
|
||||||
|
}
|
||||||
if (!srch_from) {
|
if (!srch_from) {
|
||||||
delete states;
|
delete states;
|
||||||
states = nullptr;
|
states = nullptr;
|
||||||
|
|
@ -5299,20 +5321,22 @@ Sdc::exceptionFromClkStates(const Pin *pin,
|
||||||
ExceptionStateSet *&states) const
|
ExceptionStateSet *&states) const
|
||||||
{
|
{
|
||||||
if (pin) {
|
if (pin) {
|
||||||
if (!first_from_pin_exceptions_.empty())
|
const ExceptionPathSet *exceptions =
|
||||||
exceptionFromStates(first_from_pin_exceptions_.findKey(pin),
|
findExceptions<Pin>(first_from_pin_exceptions_, pin);
|
||||||
nullptr, rf, min_max, true, states);
|
exceptionFromStates(exceptions, nullptr, rf, min_max, true, states);
|
||||||
if (!first_from_inst_exceptions_.empty()) {
|
if (!first_from_inst_exceptions_.empty()) {
|
||||||
Instance *inst = network_->instance(pin);
|
Instance *inst = network_->instance(pin);
|
||||||
exceptionFromStates(first_from_inst_exceptions_.findKey(inst),
|
const ExceptionPathSet *exceptions =
|
||||||
pin, rf, min_max, true, states);
|
findExceptions<Instance>(first_from_inst_exceptions_, inst);
|
||||||
|
exceptionFromStates(exceptions, pin, rf, min_max, true, states);
|
||||||
}
|
}
|
||||||
exceptionThruStates(first_thru_pin_exceptions_.findKey(pin),
|
const ExceptionPathSet *exceptions2 =
|
||||||
rf, min_max, states);
|
findExceptions<Pin>(first_thru_pin_exceptions_, pin);
|
||||||
|
exceptionThruStates(exceptions2, rf, min_max, states);
|
||||||
}
|
}
|
||||||
if (!first_from_clk_exceptions_.empty())
|
const ExceptionPathSet *exceptions =
|
||||||
exceptionFromStates(first_from_clk_exceptions_.findKey(clk),
|
findExceptions<Clock>(first_from_clk_exceptions_, clk);
|
||||||
pin, clk_rf, min_max, true, states);
|
exceptionFromStates(exceptions, pin, clk_rf, min_max, true, states);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -5322,10 +5346,10 @@ Sdc::filterRegQStates(const Pin *to_pin,
|
||||||
ExceptionStateSet *&states) const
|
ExceptionStateSet *&states) const
|
||||||
{
|
{
|
||||||
if (!first_from_pin_exceptions_.empty()) {
|
if (!first_from_pin_exceptions_.empty()) {
|
||||||
const ExceptionPathSet *exceptions =
|
auto itr = first_from_pin_exceptions_.find(to_pin);
|
||||||
first_from_pin_exceptions_.findKey(to_pin);
|
if (itr != first_from_pin_exceptions_.end()) {
|
||||||
if (exceptions) {
|
const ExceptionPathSet &exceptions = itr->second;
|
||||||
for (ExceptionPath *exception : *exceptions) {
|
for (ExceptionPath *exception : exceptions) {
|
||||||
// Hack for filter -from reg/Q.
|
// Hack for filter -from reg/Q.
|
||||||
if (exception->isFilter()
|
if (exception->isFilter()
|
||||||
&& exception->matchesFirstPt(to_rf, min_max)) {
|
&& exception->matchesFirstPt(to_rf, min_max)) {
|
||||||
|
|
@ -5346,19 +5370,25 @@ Sdc::exceptionThruStates(const Pin *from_pin,
|
||||||
const MinMax *min_max,
|
const MinMax *min_max,
|
||||||
ExceptionStateSet *&states) const
|
ExceptionStateSet *&states) const
|
||||||
{
|
{
|
||||||
exceptionThruStates(first_thru_pin_exceptions_.findKey(to_pin),
|
const ExceptionPathSet *exceptions =
|
||||||
to_rf, min_max, states);
|
findExceptions<Pin>(first_thru_pin_exceptions_, to_pin);
|
||||||
|
exceptionThruStates(exceptions, to_rf, min_max, states);
|
||||||
|
|
||||||
if (!first_thru_edge_exceptions_.empty()) {
|
if (!first_thru_edge_exceptions_.empty()) {
|
||||||
EdgePins edge_pins(from_pin, to_pin);
|
EdgePins edge_pins(from_pin, to_pin);
|
||||||
exceptionThruStates(first_thru_edge_exceptions_.findKey(edge_pins),
|
auto itr = first_thru_edge_exceptions_.find(edge_pins);
|
||||||
to_rf, min_max, states);
|
if (itr != first_thru_edge_exceptions_.end()) {
|
||||||
|
const ExceptionPathSet *exceptions = &itr->second;
|
||||||
|
exceptionThruStates(exceptions, to_rf, min_max, states);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!first_thru_inst_exceptions_.empty()
|
if (!first_thru_inst_exceptions_.empty()
|
||||||
&& (network_->direction(to_pin)->isAnyOutput()
|
&& (network_->direction(to_pin)->isAnyOutput()
|
||||||
|| network_->isLatchData(to_pin))) {
|
|| network_->isLatchData(to_pin))) {
|
||||||
const Instance *to_inst = network_->instance(to_pin);
|
const Instance *to_inst = network_->instance(to_pin);
|
||||||
exceptionThruStates(first_thru_inst_exceptions_.findKey(to_inst),
|
const ExceptionPathSet *exceptions =
|
||||||
to_rf, min_max, states);
|
findExceptions<Instance>(first_thru_inst_exceptions_, to_inst);
|
||||||
|
exceptionThruStates(exceptions, to_rf, min_max, states);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5396,18 +5426,26 @@ Sdc::exceptionTo(ExceptionPathType type,
|
||||||
{
|
{
|
||||||
if (!first_to_inst_exceptions_.empty()) {
|
if (!first_to_inst_exceptions_.empty()) {
|
||||||
Instance *inst = network_->instance(pin);
|
Instance *inst = network_->instance(pin);
|
||||||
exceptionTo(first_to_inst_exceptions_.findKey(inst), type, pin, rf,
|
const ExceptionPathSet *exceptions =
|
||||||
|
findExceptions<Instance>(first_to_inst_exceptions_, inst);
|
||||||
|
exceptionTo(exceptions, type, pin, rf,
|
||||||
clk_edge, min_max, match_min_max_exactly,
|
clk_edge, min_max, match_min_max_exactly,
|
||||||
hi_priority_exception, hi_priority);
|
hi_priority_exception, hi_priority);
|
||||||
}
|
}
|
||||||
if (!first_to_pin_exceptions_.empty())
|
if (!first_to_pin_exceptions_.empty()) {
|
||||||
exceptionTo(first_to_pin_exceptions_.findKey(pin), type, pin, rf,
|
const ExceptionPathSet *exceptions =
|
||||||
|
findExceptions<Pin>(first_to_pin_exceptions_, pin);
|
||||||
|
exceptionTo(exceptions, type, pin, rf,
|
||||||
clk_edge, min_max, match_min_max_exactly,
|
clk_edge, min_max, match_min_max_exactly,
|
||||||
hi_priority_exception, hi_priority);
|
hi_priority_exception, hi_priority);
|
||||||
if (clk_edge && !first_to_clk_exceptions_.empty())
|
}
|
||||||
exceptionTo(first_to_clk_exceptions_.findKey(clk_edge->clock()),
|
if (clk_edge && !first_to_clk_exceptions_.empty()) {
|
||||||
type, pin, rf, clk_edge, min_max, match_min_max_exactly,
|
const ExceptionPathSet *exceptions =
|
||||||
|
findExceptions<Clock>(first_to_clk_exceptions_, clk_edge->clock());
|
||||||
|
exceptionTo(exceptions, type, pin, rf, clk_edge,
|
||||||
|
min_max, match_min_max_exactly,
|
||||||
hi_priority_exception, hi_priority);
|
hi_priority_exception, hi_priority);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -5515,15 +5553,20 @@ Sdc::groupPathsTo(const Pin *pin,
|
||||||
{
|
{
|
||||||
if (!first_to_inst_exceptions_.empty()) {
|
if (!first_to_inst_exceptions_.empty()) {
|
||||||
Instance *inst = network_->instance(pin);
|
Instance *inst = network_->instance(pin);
|
||||||
groupPathsTo(first_to_inst_exceptions_.findKey(inst), pin, rf,
|
const ExceptionPathSet *exceptions =
|
||||||
clk_edge, min_max, group_paths);
|
findExceptions<Instance>(first_to_inst_exceptions_, inst);
|
||||||
|
groupPathsTo(exceptions, pin, rf, clk_edge, min_max, group_paths);
|
||||||
|
}
|
||||||
|
if (!first_to_pin_exceptions_.empty()) {
|
||||||
|
const ExceptionPathSet *exceptions =
|
||||||
|
findExceptions<Pin>(first_to_pin_exceptions_, pin);
|
||||||
|
groupPathsTo(exceptions, pin, rf, clk_edge, min_max, group_paths);
|
||||||
|
}
|
||||||
|
if (clk_edge && !first_to_clk_exceptions_.empty()) {
|
||||||
|
const ExceptionPathSet *exceptions =
|
||||||
|
findExceptions<Clock>(first_to_clk_exceptions_, clk_edge->clock());
|
||||||
|
groupPathsTo(exceptions, 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
|
void
|
||||||
|
|
@ -5627,7 +5670,7 @@ Sdc::disconnectPinBefore(const Pin *pin)
|
||||||
{
|
{
|
||||||
auto itr = pin_exceptions_.find(pin);
|
auto itr = pin_exceptions_.find(pin);
|
||||||
if (itr != pin_exceptions_.end()) {
|
if (itr != pin_exceptions_.end()) {
|
||||||
for (ExceptionPath *exception : *itr->second) {
|
for (ExceptionPath *exception : itr->second) {
|
||||||
ExceptionFrom *from = exception->from();
|
ExceptionFrom *from = exception->from();
|
||||||
if (from)
|
if (from)
|
||||||
from->disconnectPinBefore(pin, network_);
|
from->disconnectPinBefore(pin, network_);
|
||||||
|
|
|
||||||
13
sdc/Sdc.i
13
sdc/Sdc.i
|
|
@ -1355,19 +1355,6 @@ set_voltage_net(const Net *net,
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
PinSet
|
|
||||||
group_path_pins(const char *group_path_name)
|
|
||||||
{
|
|
||||||
Sta *sta = Sta::sta();
|
|
||||||
Sdc *sdc = sta->sdc();
|
|
||||||
if (sdc->isGroupPathName(group_path_name))
|
|
||||||
return sta->findGroupPathPins(group_path_name);
|
|
||||||
else
|
|
||||||
return PinSet(sta->network());
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
char
|
char
|
||||||
pin_case_logic_value(const Pin *pin)
|
pin_case_logic_value(const Pin *pin)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
31
sdc/Sdc.tcl
31
sdc/Sdc.tcl
|
|
@ -578,7 +578,12 @@ proc get_lib_pins { args } {
|
||||||
}
|
}
|
||||||
set libcells [get_libcells_error "objects" $keys(-of_objects)]
|
set libcells [get_libcells_error "objects" $keys(-of_objects)]
|
||||||
foreach libcell $libcells {
|
foreach libcell $libcells {
|
||||||
lappend ports {*}[$libcell find_liberty_ports_matching * 0 1]
|
foreach port [$libcell find_liberty_ports_matching * 0 1] {
|
||||||
|
# Filter pg ports.
|
||||||
|
if { ![$port is_pwr_gnd] } {
|
||||||
|
lappend ports $port
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
foreach pattern $patterns {
|
foreach pattern $patterns {
|
||||||
|
|
@ -610,8 +615,11 @@ proc get_lib_pins { args } {
|
||||||
set matches [$cell find_liberty_ports_matching $port_pattern \
|
set matches [$cell find_liberty_ports_matching $port_pattern \
|
||||||
$regexp $nocase]
|
$regexp $nocase]
|
||||||
foreach match $matches {
|
foreach match $matches {
|
||||||
lappend ports $match
|
# Filter pg ports.
|
||||||
set found_match 1
|
if { ![$match is_pwr_gnd] } {
|
||||||
|
lappend ports $match
|
||||||
|
set found_match 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -814,7 +822,10 @@ proc get_pins { args } {
|
||||||
set pin_iter [$inst pin_iterator]
|
set pin_iter [$inst pin_iterator]
|
||||||
while { [$pin_iter has_next] } {
|
while { [$pin_iter has_next] } {
|
||||||
set pin [$pin_iter next]
|
set pin [$pin_iter next]
|
||||||
lappend pins $pin
|
# Filter pg ports.
|
||||||
|
if { ![$pin is_pwr_gnd] } {
|
||||||
|
lappend pins $pin
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$pin_iter finish
|
$pin_iter finish
|
||||||
}
|
}
|
||||||
|
|
@ -822,7 +833,10 @@ proc get_pins { args } {
|
||||||
set pin_iter [$net pin_iterator]
|
set pin_iter [$net pin_iterator]
|
||||||
while { [$pin_iter has_next] } {
|
while { [$pin_iter has_next] } {
|
||||||
set pin [$pin_iter next]
|
set pin [$pin_iter next]
|
||||||
lappend pins $pin
|
# Filter pg ports.
|
||||||
|
if { ![$pin is_pwr_gnd] } {
|
||||||
|
lappend pins $pin
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$pin_iter finish
|
$pin_iter finish
|
||||||
}
|
}
|
||||||
|
|
@ -852,7 +866,12 @@ proc get_pins { args } {
|
||||||
} else {
|
} else {
|
||||||
set matches [find_pins_matching $pattern $regexp $nocase]
|
set matches [find_pins_matching $pattern $regexp $nocase]
|
||||||
}
|
}
|
||||||
set pins [concat $pins $matches]
|
foreach match $matches {
|
||||||
|
# Filter pg ports.
|
||||||
|
if { ![$match is_pwr_gnd] } {
|
||||||
|
lappend pins $match
|
||||||
|
}
|
||||||
|
}
|
||||||
if { $matches == {} && !$quiet } {
|
if { $matches == {} && !$quiet } {
|
||||||
sta_warn 363 "pin '$pattern' not found."
|
sta_warn 363 "pin '$pattern' not found."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1196,7 +1196,7 @@ void
|
||||||
WriteSdc::writeExceptions() const
|
WriteSdc::writeExceptions() const
|
||||||
{
|
{
|
||||||
ExceptionPathSeq exceptions;
|
ExceptionPathSeq exceptions;
|
||||||
for (ExceptionPath *exception : *sdc_->exceptions())
|
for (ExceptionPath *exception : sdc_->exceptions())
|
||||||
exceptions.push_back(exception);
|
exceptions.push_back(exception);
|
||||||
sort(exceptions, ExceptionPathLess(network_));
|
sort(exceptions, ExceptionPathLess(network_));
|
||||||
for (ExceptionPath *exception : exceptions) {
|
for (ExceptionPath *exception : exceptions) {
|
||||||
|
|
|
||||||
|
|
@ -514,42 +514,44 @@ SdfWriter::writeTimingChecks(const Instance *inst,
|
||||||
while (pin_iter->hasNext()) {
|
while (pin_iter->hasNext()) {
|
||||||
Pin *pin = pin_iter->next();
|
Pin *pin = pin_iter->next();
|
||||||
Vertex *vertex = graph_->pinLoadVertex(pin);
|
Vertex *vertex = graph_->pinLoadVertex(pin);
|
||||||
VertexOutEdgeIterator edge_iter(vertex, graph_);
|
if (vertex) {
|
||||||
while (edge_iter.hasNext()) {
|
VertexOutEdgeIterator edge_iter(vertex, graph_);
|
||||||
Edge *edge = edge_iter.next();
|
while (edge_iter.hasNext()) {
|
||||||
const TimingRole *role = edge->role();
|
Edge *edge = edge_iter.next();
|
||||||
const char *sdf_check = nullptr;
|
const TimingRole *role = edge->role();
|
||||||
if (role == TimingRole::setup())
|
const char *sdf_check = nullptr;
|
||||||
sdf_check = "SETUP";
|
if (role == TimingRole::setup())
|
||||||
else if (role == TimingRole::hold())
|
sdf_check = "SETUP";
|
||||||
sdf_check = "HOLD";
|
else if (role == TimingRole::hold())
|
||||||
else if (role == TimingRole::recovery())
|
sdf_check = "HOLD";
|
||||||
sdf_check = "RECOVERY";
|
else if (role == TimingRole::recovery())
|
||||||
else if (role == TimingRole::removal())
|
sdf_check = "RECOVERY";
|
||||||
sdf_check = "REMOVAL";
|
else if (role == TimingRole::removal())
|
||||||
if (sdf_check) {
|
sdf_check = "REMOVAL";
|
||||||
ensureTimingCheckheaders(check_header, inst, inst_header);
|
if (sdf_check) {
|
||||||
writeCheck(edge, sdf_check);
|
ensureTimingCheckheaders(check_header, inst, inst_header);
|
||||||
|
writeCheck(edge, sdf_check);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
for (auto hi_low : RiseFall::range()) {
|
||||||
for (auto hi_low : RiseFall::range()) {
|
float min_width, max_width;
|
||||||
float min_width, max_width;
|
Edge *edge;
|
||||||
Edge *edge;
|
TimingArc *arc;
|
||||||
TimingArc *arc;
|
graph_->minPulseWidthArc(vertex, hi_low, edge, arc);
|
||||||
graph_->minPulseWidthArc(vertex, hi_low, edge, arc);
|
if (edge) {
|
||||||
if (edge) {
|
min_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_));
|
||||||
min_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_min_index_));
|
max_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_max_index_));
|
||||||
max_width = delayAsFloat(graph_->arcDelay(edge, arc, arc_delay_max_index_));
|
ensureTimingCheckheaders(check_header, inst, inst_header);
|
||||||
ensureTimingCheckheaders(check_header, inst, inst_header);
|
writeWidthCheck(pin, hi_low, min_width, max_width);
|
||||||
writeWidthCheck(pin, hi_low, min_width, max_width);
|
}
|
||||||
|
}
|
||||||
|
float min_period;
|
||||||
|
bool exists;
|
||||||
|
graph_delay_calc_->minPeriod(pin, corner_, min_period, exists);
|
||||||
|
if (exists) {
|
||||||
|
ensureTimingCheckheaders(check_header, inst, inst_header);
|
||||||
|
writePeriodCheck(pin, min_period);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
float min_period;
|
|
||||||
bool exists;
|
|
||||||
graph_delay_calc_->minPeriod(pin, corner_, min_period, exists);
|
|
||||||
if (exists) {
|
|
||||||
ensureTimingCheckheaders(check_header, inst, inst_header);
|
|
||||||
writePeriodCheck(pin, min_period);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete pin_iter;
|
delete pin_iter;
|
||||||
|
|
|
||||||
|
|
@ -273,10 +273,7 @@ CheckTiming::hasClkedDepature(Pin *pin)
|
||||||
bool
|
bool
|
||||||
CheckTiming::hasMaxDelay(Pin *pin)
|
CheckTiming::hasMaxDelay(Pin *pin)
|
||||||
{
|
{
|
||||||
ExceptionPathSet *exceptions = sdc_->exceptions();
|
for (ExceptionPath *exception : sdc_->exceptions()) {
|
||||||
ExceptionPathSet::Iterator exception_iter(exceptions);
|
|
||||||
while (exception_iter.hasNext()) {
|
|
||||||
ExceptionPath *exception = exception_iter.next();
|
|
||||||
ExceptionTo *to = exception->to();
|
ExceptionTo *to = exception->to();
|
||||||
if (exception->isPathDelay()
|
if (exception->isPathDelay()
|
||||||
&& exception->minMax() == MinMaxAll::max()
|
&& exception->minMax() == MinMaxAll::max()
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,6 @@
|
||||||
#include "ClkLatency.hh"
|
#include "ClkLatency.hh"
|
||||||
#include "FindRegister.hh"
|
#include "FindRegister.hh"
|
||||||
#include "ReportPath.hh"
|
#include "ReportPath.hh"
|
||||||
#include "VisitPathGroupVertices.hh"
|
|
||||||
#include "Genclks.hh"
|
#include "Genclks.hh"
|
||||||
#include "ClkNetwork.hh"
|
#include "ClkNetwork.hh"
|
||||||
#include "power/Power.hh"
|
#include "power/Power.hh"
|
||||||
|
|
@ -2721,36 +2720,6 @@ Sta::endpointViolationCount(const MinMax *min_max)
|
||||||
return violations;
|
return violations;
|
||||||
}
|
}
|
||||||
|
|
||||||
PinSet
|
|
||||||
Sta::findGroupPathPins(const char *group_path_name)
|
|
||||||
{
|
|
||||||
if (!(search_->havePathGroups()
|
|
||||||
&& search_->arrivalsValid())) {
|
|
||||||
PathEndSeq path_ends = findPathEnds(// from, thrus, to, unconstrained
|
|
||||||
nullptr, nullptr, nullptr, false,
|
|
||||||
// corner, min_max,
|
|
||||||
nullptr, MinMaxAll::max(),
|
|
||||||
// group_path_count, endpoint_path_count
|
|
||||||
1, 1,
|
|
||||||
// unique_pins, unique_edges
|
|
||||||
true, true,
|
|
||||||
-INF, INF, // slack_min, slack_max,
|
|
||||||
false, // sort_by_slack
|
|
||||||
nullptr, // group_names
|
|
||||||
// setup, hold, recovery, removal,
|
|
||||||
true, true, true, true,
|
|
||||||
// clk_gating_setup, clk_gating_hold
|
|
||||||
true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
PathGroup *path_group = search_->findPathGroup(group_path_name,
|
|
||||||
MinMax::max());
|
|
||||||
PinSet pins(network_);
|
|
||||||
VertexPinCollector visitor(pins);
|
|
||||||
visitPathGroupVertices(path_group, &visitor, this);
|
|
||||||
return pins;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -4303,7 +4272,8 @@ Sta::replaceEquivCellBefore(const Instance *inst,
|
||||||
else {
|
else {
|
||||||
// Force delay calculation on output pins.
|
// Force delay calculation on output pins.
|
||||||
Vertex *vertex = graph_->pinDrvrVertex(pin);
|
Vertex *vertex = graph_->pinDrvrVertex(pin);
|
||||||
graph_delay_calc_->delayInvalid(vertex);
|
if (vertex)
|
||||||
|
graph_delay_calc_->delayInvalid(vertex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4618,6 +4588,7 @@ Sta::deleteLeafInstanceBefore(const Instance *inst)
|
||||||
{
|
{
|
||||||
sim_->deleteInstanceBefore(inst);
|
sim_->deleteInstanceBefore(inst);
|
||||||
sdc_->deleteInstanceBefore(inst);
|
sdc_->deleteInstanceBefore(inst);
|
||||||
|
power_->deleteInstanceBefore(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -4694,6 +4665,7 @@ Sta::deletePinBefore(const Pin *pin)
|
||||||
}
|
}
|
||||||
sim_->deletePinBefore(pin);
|
sim_->deletePinBefore(pin);
|
||||||
clk_network_->deletePinBefore(pin);
|
clk_network_->deletePinBefore(pin);
|
||||||
|
power_->deletePinBefore(pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -1,313 +0,0 @@
|
||||||
// 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 <https://www.gnu.org/licenses/>.
|
|
||||||
//
|
|
||||||
// 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 "VisitPathGroupVertices.hh"
|
|
||||||
|
|
||||||
#include "Debug.hh"
|
|
||||||
#include "Graph.hh"
|
|
||||||
#include "Bfs.hh"
|
|
||||||
#include "Search.hh"
|
|
||||||
#include "Path.hh"
|
|
||||||
#include "PathEnd.hh"
|
|
||||||
#include "Tag.hh"
|
|
||||||
#include "VisitPathEnds.hh"
|
|
||||||
|
|
||||||
namespace sta {
|
|
||||||
|
|
||||||
typedef Set<Path*, PathLess> PathSet;
|
|
||||||
typedef Map<Vertex*, PathSet*> VertexPathSetMap;
|
|
||||||
|
|
||||||
static void
|
|
||||||
vertexPathSetMapInsertPath(VertexPathSetMap *matching_path_map,
|
|
||||||
Vertex *vertex,
|
|
||||||
Tag *tag,
|
|
||||||
const StaState *sta);
|
|
||||||
|
|
||||||
// Visit each path end for a vertex and add the worst one in each
|
|
||||||
// path group to the group.
|
|
||||||
class VisitPathGroupEnds : public PathEndVisitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
VisitPathGroupEnds(PathGroup *path_group,
|
|
||||||
VertexVisitor *vertex_visitor,
|
|
||||||
VertexPathSetMap *matching_path_map,
|
|
||||||
BfsBkwdIterator *bkwd_iter,
|
|
||||||
StaState *sta);
|
|
||||||
VisitPathGroupEnds(const VisitPathGroupEnds&) = default;
|
|
||||||
virtual PathEndVisitor *copy() const;
|
|
||||||
virtual void visit(PathEnd *path_end);
|
|
||||||
virtual void vertexBegin(Vertex *vertex);
|
|
||||||
virtual void vertexEnd(Vertex *vertex);
|
|
||||||
|
|
||||||
private:
|
|
||||||
PathGroup *path_group_;
|
|
||||||
VertexVisitor *vertex_visitor_;
|
|
||||||
BfsBkwdIterator *bkwd_iter_;
|
|
||||||
VertexPathSetMap *matching_path_map_;
|
|
||||||
bool vertex_matches_;
|
|
||||||
StaState *sta_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PathGroupPathVisitor : public PathVisitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PathGroupPathVisitor(VertexVisitor *visitor,
|
|
||||||
BfsBkwdIterator *bkwd_iter,
|
|
||||||
VertexPathSetMap *matching_path_map,
|
|
||||||
const StaState *sta);
|
|
||||||
virtual ~PathGroupPathVisitor();
|
|
||||||
virtual VertexVisitor *copy() const;
|
|
||||||
virtual void visit(Vertex *vertex);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// Return false to stop visiting.
|
|
||||||
virtual bool visitFromToPath(const Pin *from_pin,
|
|
||||||
Vertex *from_vertex,
|
|
||||||
const RiseFall *from_rf,
|
|
||||||
Tag *from_tag,
|
|
||||||
Path *from_path,
|
|
||||||
const Arrival &from_arrival,
|
|
||||||
Edge *edge,
|
|
||||||
TimingArc *arc,
|
|
||||||
ArcDelay arc_delay,
|
|
||||||
Vertex *to_vertex,
|
|
||||||
const RiseFall *to_rf,
|
|
||||||
Tag *to_tag,
|
|
||||||
Arrival &to_arrival,
|
|
||||||
const MinMax *min_max,
|
|
||||||
const PathAnalysisPt *path_ap);
|
|
||||||
void fromMatches(Vertex *from_vertex,
|
|
||||||
Tag *from_tag);
|
|
||||||
|
|
||||||
private:
|
|
||||||
VertexVisitor *visitor_;
|
|
||||||
BfsBkwdIterator *bkwd_iter_;
|
|
||||||
VertexPathSetMap *matching_path_map_;
|
|
||||||
bool vertex_matches_;
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Visit the fanin vertices for the path group.
|
|
||||||
// Vertices in the clock network are NOT visited.
|
|
||||||
void
|
|
||||||
visitPathGroupVertices(PathGroup *path_group,
|
|
||||||
VertexVisitor *visitor,
|
|
||||||
StaState *sta)
|
|
||||||
{
|
|
||||||
Search *search = sta->search();
|
|
||||||
VertexPathSetMap matching_path_map;
|
|
||||||
// Do not visit clock network.
|
|
||||||
SearchPredNonReg2 srch_non_reg(sta);
|
|
||||||
BfsBkwdIterator bkwd_iter(BfsIndex::other, &srch_non_reg, sta);
|
|
||||||
// Visit the path ends and filter by path_group to seed the backward search.
|
|
||||||
VisitPathGroupEnds end_visitor(path_group, visitor, &matching_path_map,
|
|
||||||
&bkwd_iter, sta);
|
|
||||||
VisitPathEnds visit_path_ends(sta);
|
|
||||||
for(Vertex *vertex : *search->endpoints())
|
|
||||||
visit_path_ends.visitPathEnds(vertex, &end_visitor);
|
|
||||||
|
|
||||||
// Search backward from the path ends thru vertices that have arrival tags
|
|
||||||
// that match path_group end paths.
|
|
||||||
PathGroupPathVisitor path_visitor(visitor, &bkwd_iter, &matching_path_map,
|
|
||||||
sta);
|
|
||||||
bkwd_iter.visit(0, &path_visitor);
|
|
||||||
|
|
||||||
// Cleanup.
|
|
||||||
VertexPathSetMap::Iterator matching_iter(matching_path_map);
|
|
||||||
while (matching_iter.hasNext()) {
|
|
||||||
PathSet *paths = matching_iter.next();
|
|
||||||
PathSet::Iterator path_iter(paths);
|
|
||||||
while (path_iter.hasNext()) {
|
|
||||||
Path *path = path_iter.next();
|
|
||||||
delete path;
|
|
||||||
}
|
|
||||||
delete paths;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
VisitPathGroupEnds::VisitPathGroupEnds(PathGroup *path_group,
|
|
||||||
VertexVisitor *vertex_visitor,
|
|
||||||
VertexPathSetMap *matching_path_map,
|
|
||||||
BfsBkwdIterator *bkwd_iter,
|
|
||||||
StaState *sta) :
|
|
||||||
path_group_(path_group),
|
|
||||||
vertex_visitor_(vertex_visitor),
|
|
||||||
bkwd_iter_(bkwd_iter),
|
|
||||||
matching_path_map_(matching_path_map),
|
|
||||||
sta_(sta)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
PathEndVisitor *
|
|
||||||
VisitPathGroupEnds::copy() const
|
|
||||||
{
|
|
||||||
return new VisitPathGroupEnds(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VisitPathGroupEnds::vertexBegin(Vertex *)
|
|
||||||
{
|
|
||||||
vertex_matches_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VisitPathGroupEnds::visit(PathEnd *path_end)
|
|
||||||
{
|
|
||||||
PathGroupSeq groups = sta_->search()->pathGroups(path_end);
|
|
||||||
for (PathGroup *group : groups) {
|
|
||||||
if (group == path_group_) {
|
|
||||||
Path *path = path_end->path();
|
|
||||||
Vertex *vertex = path->vertex(sta_);
|
|
||||||
vertexPathSetMapInsertPath(matching_path_map_, vertex, path->tag(sta_), sta_);
|
|
||||||
vertex_matches_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
vertexPathSetMapInsertPath(VertexPathSetMap *matching_path_map,
|
|
||||||
Vertex *vertex,
|
|
||||||
Tag *tag,
|
|
||||||
const StaState *sta)
|
|
||||||
{
|
|
||||||
PathSet *matching_paths = matching_path_map->findKey(vertex);
|
|
||||||
if (matching_paths == nullptr) {
|
|
||||||
PathLess path_less(sta);
|
|
||||||
matching_paths = new PathSet(path_less);
|
|
||||||
(*matching_path_map)[vertex] = matching_paths;
|
|
||||||
}
|
|
||||||
Path *vpath = new Path(vertex, tag, sta);
|
|
||||||
matching_paths->insert(vpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VisitPathGroupEnds::vertexEnd(Vertex *vertex)
|
|
||||||
{
|
|
||||||
if (vertex_matches_) {
|
|
||||||
vertex_visitor_->visit(vertex);
|
|
||||||
// Seed backward bfs fanin search.
|
|
||||||
bkwd_iter_->enqueueAdjacentVertices(vertex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
PathGroupPathVisitor::PathGroupPathVisitor(VertexVisitor *visitor,
|
|
||||||
BfsBkwdIterator *bkwd_iter,
|
|
||||||
VertexPathSetMap *matching_path_map,
|
|
||||||
const StaState *sta) :
|
|
||||||
PathVisitor(sta),
|
|
||||||
visitor_(visitor),
|
|
||||||
bkwd_iter_(bkwd_iter),
|
|
||||||
matching_path_map_(matching_path_map)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
PathGroupPathVisitor::~PathGroupPathVisitor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
VertexVisitor *
|
|
||||||
PathGroupPathVisitor::copy() const
|
|
||||||
{
|
|
||||||
return new PathGroupPathVisitor(visitor_, bkwd_iter_,
|
|
||||||
matching_path_map_, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PathGroupPathVisitor::visit(Vertex *vertex)
|
|
||||||
{
|
|
||||||
vertex_matches_ = false;
|
|
||||||
visitFanoutPaths(vertex);
|
|
||||||
if (vertex_matches_) {
|
|
||||||
debugPrint(debug_, "visit_path_group", 1, "visit %s",
|
|
||||||
vertex->to_string(this).c_str());
|
|
||||||
visitor_->visit(vertex);
|
|
||||||
bkwd_iter_->enqueueAdjacentVertices(vertex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
PathGroupPathVisitor::visitFromToPath(const Pin *,
|
|
||||||
Vertex *from_vertex,
|
|
||||||
const RiseFall *,
|
|
||||||
Tag *from_tag,
|
|
||||||
Path *,
|
|
||||||
const Arrival &,
|
|
||||||
Edge *,
|
|
||||||
TimingArc *,
|
|
||||||
ArcDelay ,
|
|
||||||
Vertex *to_vertex,
|
|
||||||
const RiseFall *to_rf,
|
|
||||||
Tag *to_tag,
|
|
||||||
Arrival &,
|
|
||||||
const MinMax *,
|
|
||||||
const PathAnalysisPt *path_ap)
|
|
||||||
{
|
|
||||||
PathSet *matching_paths = matching_path_map_->findKey(to_vertex);
|
|
||||||
if (matching_paths) {
|
|
||||||
Path to_path(to_vertex, to_tag, this);
|
|
||||||
if (!to_path.isNull()) {
|
|
||||||
if (matching_paths->hasKey(&to_path)) {
|
|
||||||
debugPrint(debug_, "visit_path_group", 2, "match %s %s -> %s %s",
|
|
||||||
from_vertex->to_string(this).c_str(),
|
|
||||||
from_tag->to_string(this).c_str(),
|
|
||||||
to_vertex->to_string(this).c_str(),
|
|
||||||
to_tag->to_string(this).c_str());
|
|
||||||
fromMatches(from_vertex, from_tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
VertexPathIterator to_iter(to_vertex, to_rf, path_ap, this);
|
|
||||||
while (to_iter.hasNext()) {
|
|
||||||
Path *to_path = to_iter.next();
|
|
||||||
if (Tag::matchNoCrpr(to_path->tag(this), to_tag)
|
|
||||||
&& matching_paths->hasKey(to_path)) {
|
|
||||||
debugPrint(debug_, "visit_path_group", 2,
|
|
||||||
"match crpr %s %s -> %s %s",
|
|
||||||
from_vertex->to_string(this).c_str(),
|
|
||||||
from_tag->to_string(this).c_str(),
|
|
||||||
to_vertex->to_string(this).c_str(),
|
|
||||||
to_tag->to_string(this).c_str());
|
|
||||||
fromMatches(from_vertex, from_tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PathGroupPathVisitor::fromMatches(Vertex *from_vertex,
|
|
||||||
Tag *from_tag)
|
|
||||||
{
|
|
||||||
vertex_matches_ = true;
|
|
||||||
vertexPathSetMapInsertPath(matching_path_map_, from_vertex,
|
|
||||||
from_tag, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
// 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 <https://www.gnu.org/licenses/>.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace sta {
|
|
||||||
|
|
||||||
class PathGroup;
|
|
||||||
class VertexVisitor;
|
|
||||||
class StaState;
|
|
||||||
|
|
||||||
// Visit the fanin vertices for the path group.
|
|
||||||
// Vertices in the clock network are NOT visited.
|
|
||||||
void
|
|
||||||
visitPathGroupVertices(PathGroup *path_group,
|
|
||||||
VertexVisitor *visitor,
|
|
||||||
StaState *sta);
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
@ -261,7 +261,7 @@ WriteSpice::recordSpicePortNames(const char *cell_name,
|
||||||
for (size_t i = 2; i < tokens.size(); i++) {
|
for (size_t i = 2; i < tokens.size(); i++) {
|
||||||
const char *port_name = tokens[i].c_str();
|
const char *port_name = tokens[i].c_str();
|
||||||
LibertyPort *port = cell->findLibertyPort(port_name);
|
LibertyPort *port = cell->findLibertyPort(port_name);
|
||||||
LibertyPgPort *pg_port = cell->findPgPort(port_name);
|
LibertyPort *pg_port = cell->findLibertyPort(port_name);
|
||||||
if (port == nullptr
|
if (port == nullptr
|
||||||
&& pg_port == nullptr
|
&& pg_port == nullptr
|
||||||
&& !stringEqual(port_name, power_name_)
|
&& !stringEqual(port_name, power_name_)
|
||||||
|
|
@ -326,7 +326,7 @@ WriteSpice::writeSubcktInst(const Instance *inst)
|
||||||
for (string subckt_port_name : spice_port_names) {
|
for (string subckt_port_name : spice_port_names) {
|
||||||
const char *subckt_port_cname = subckt_port_name.c_str();
|
const char *subckt_port_cname = subckt_port_name.c_str();
|
||||||
Pin *pin = network_->findPin(inst, subckt_port_cname);
|
Pin *pin = network_->findPin(inst, subckt_port_cname);
|
||||||
LibertyPgPort *pg_port = cell->findPgPort(subckt_port_cname);
|
LibertyPort *pg_port = cell->findLibertyPort(subckt_port_cname);
|
||||||
const char *pin_name;
|
const char *pin_name;
|
||||||
if (pin) {
|
if (pin) {
|
||||||
pin_name = network_->pathName(pin);
|
pin_name = network_->pathName(pin);
|
||||||
|
|
@ -357,13 +357,13 @@ WriteSpice::writeSubcktInstVoltSrcs(const Instance *inst,
|
||||||
const char *subckt_port_name = subckt_port_sname.c_str();
|
const char *subckt_port_name = subckt_port_sname.c_str();
|
||||||
LibertyPort *port = cell->findLibertyPort(subckt_port_name);
|
LibertyPort *port = cell->findLibertyPort(subckt_port_name);
|
||||||
const Pin *pin = port ? network_->findPin(inst, port) : nullptr;
|
const Pin *pin = port ? network_->findPin(inst, port) : nullptr;
|
||||||
LibertyPgPort *pg_port = cell->findPgPort(subckt_port_name);
|
bool is_pg_port = port && port->isPwrGnd();
|
||||||
debugPrint(debug_, "write_spice", 2, " port %s%s",
|
debugPrint(debug_, "write_spice", 2, " port %s%s",
|
||||||
subckt_port_name,
|
subckt_port_name,
|
||||||
pg_port ? " pwr/gnd" : "");
|
is_pg_port ? " pwr/gnd" : "");
|
||||||
if (pg_port)
|
if (is_pg_port)
|
||||||
writeVoltageSource(inst_name, subckt_port_name,
|
writeVoltageSource(inst_name, subckt_port_name,
|
||||||
pgPortVoltage(pg_port));
|
pgPortVoltage(port));
|
||||||
else if (stringEq(subckt_port_name, power_name_))
|
else if (stringEq(subckt_port_name, power_name_))
|
||||||
writeVoltageSource(inst_name, subckt_port_name, power_voltage_);
|
writeVoltageSource(inst_name, subckt_port_name, power_voltage_);
|
||||||
else if (stringEq(subckt_port_name, gnd_name_))
|
else if (stringEq(subckt_port_name, gnd_name_))
|
||||||
|
|
@ -420,7 +420,7 @@ WriteSpice::writeVoltageSource(LibertyCell *cell,
|
||||||
float voltage)
|
float voltage)
|
||||||
{
|
{
|
||||||
if (pg_port_name) {
|
if (pg_port_name) {
|
||||||
LibertyPgPort *pg_port = cell->findPgPort(pg_port_name);
|
LibertyPort *pg_port = cell->findLibertyPort(pg_port_name);
|
||||||
if (pg_port)
|
if (pg_port)
|
||||||
voltage = pgPortVoltage(pg_port);
|
voltage = pgPortVoltage(pg_port);
|
||||||
else
|
else
|
||||||
|
|
@ -433,9 +433,9 @@ WriteSpice::writeVoltageSource(LibertyCell *cell,
|
||||||
}
|
}
|
||||||
|
|
||||||
float
|
float
|
||||||
WriteSpice::pgPortVoltage(LibertyPgPort *pg_port)
|
WriteSpice::pgPortVoltage(LibertyPort *pg_port)
|
||||||
{
|
{
|
||||||
LibertyLibrary *liberty = pg_port->cell()->libertyLibrary();
|
LibertyLibrary *liberty = pg_port->libertyCell()->libertyLibrary();
|
||||||
float voltage = 0.0;
|
float voltage = 0.0;
|
||||||
bool exists;
|
bool exists;
|
||||||
const char *voltage_name = pg_port->voltageName();
|
const char *voltage_name = pg_port->voltageName();
|
||||||
|
|
@ -448,14 +448,14 @@ WriteSpice::pgPortVoltage(LibertyPgPort *pg_port)
|
||||||
voltage = gnd_voltage_;
|
voltage = gnd_voltage_;
|
||||||
else
|
else
|
||||||
report_->error(1601 , "pg_pin %s/%s voltage %s not found,",
|
report_->error(1601 , "pg_pin %s/%s voltage %s not found,",
|
||||||
pg_port->cell()->name(),
|
pg_port->libertyCell()->name(),
|
||||||
pg_port->name(),
|
pg_port->name(),
|
||||||
voltage_name);
|
voltage_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
report_->error(1602, "Liberty pg_port %s/%s missing voltage_name attribute,",
|
report_->error(1602, "Liberty pg_port %s/%s missing voltage_name attribute,",
|
||||||
pg_port->cell()->name(),
|
pg_port->libertyCell()->name(),
|
||||||
pg_port->name());
|
pg_port->name());
|
||||||
return voltage;
|
return voltage;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ protected:
|
||||||
void writeSubcktInstVoltSrcs(const Instance *inst,
|
void writeSubcktInstVoltSrcs(const Instance *inst,
|
||||||
LibertyPortLogicValues &port_values,
|
LibertyPortLogicValues &port_values,
|
||||||
const PinSet &excluded_input_pins);
|
const PinSet &excluded_input_pins);
|
||||||
float pgPortVoltage(LibertyPgPort *pg_port);
|
float pgPortVoltage(LibertyPort *pg_port);
|
||||||
void writeVoltageSource(const char *inst_name,
|
void writeVoltageSource(const char *inst_name,
|
||||||
const char *port_name,
|
const char *port_name,
|
||||||
float voltage);
|
float voltage);
|
||||||
|
|
|
||||||
|
|
@ -1900,19 +1900,11 @@ VerilogReader::makeNamedInstPins(Cell *cell,
|
||||||
delete net_name_iter;
|
delete net_name_iter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
LibertyPgPort *pg_port = nullptr;
|
linkWarn(201, parent_module->filename(), mod_inst->line(),
|
||||||
LibertyCell *lib_cell = network_->libertyCell(cell);
|
"instance %s port %s not found.",
|
||||||
if (lib_cell)
|
inst_vname.c_str(),
|
||||||
pg_port = lib_cell->findPgPort(port_name);
|
port_name);
|
||||||
// Do not warn about connections to pg ports (which are ignored).
|
|
||||||
if (pg_port == nullptr) {
|
|
||||||
linkWarn(201, parent_module->filename(), mod_inst->line(),
|
|
||||||
"instance %s port %s not found.",
|
|
||||||
inst_vname.c_str(),
|
|
||||||
port_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue