mirror of
https://github.com/The-OpenROAD-Project/OpenSTA.git
synced 2026-09-01 18:36:42 +02:00
commit d122d05822e02dcc08c665ac6ec7513791dd7209 Author: James Cherry <[email protected]> Date: Thu Mar 27 08:58:22 2025 -0700 rebase Signed-off-by: James Cherry <[email protected]> commit 9c7ae9a7ddd885ebdab102d48b3f39dc5dacf948 Author: James Cherry <[email protected]> Date: Tue Mar 25 16:21:52 2025 -0700 write_spice8 Signed-off-by: James Cherry <[email protected]> commit 2bd088f03bb2e414305232d9ebd76c9d1958ec81 Author: James Cherry <[email protected]> Date: Tue Mar 25 10:08:00 2025 -0700 liberty reader stringify Signed-off-by: James Cherry <[email protected]> commit 86974caf063433b37ed1378e7103db4b2e55a04c Author: James Cherry <[email protected]> Date: Mon Mar 24 20:25:39 2025 -0700 ConcreteLiberary/Cell/Port use string Signed-off-by: James Cherry <[email protected]> commit 334476e185149a90b35cdd859e0a760ec9aa242a Author: James Cherry <[email protected]> Date: Mon Mar 24 20:16:08 2025 -0700 leak Signed-off-by: James Cherry <[email protected]> commit 5130e8d44804f483d9099d48bb413a7f3362b4e1 Author: James Cherry <[email protected]> Date: Mon Mar 24 15:57:14 2025 -0700 liberty parser stringify Signed-off-by: James Cherry <[email protected]> commit d48eba88cbde9093e3eb12bcee8eb48ccd444434 Author: James Cherry <[email protected]> Date: Mon Mar 24 11:16:04 2025 -0700 stringify Signed-off-by: James Cherry <[email protected]> commit 6913fb198d642f6b05a94fb1852064706a748b81 Author: James Cherry <[email protected]> Date: Mon Mar 24 11:06:17 2025 -0700 stringify Signed-off-by: James Cherry <[email protected]> commit 371bca08ecf9bf816b7adcbb7ae1458c4073f5f8 Author: James Cherry <[email protected]> Date: Mon Mar 24 10:44:31 2025 -0700 TableTemplate use string Signed-off-by: James Cherry <[email protected]> commit 326465920a1f4a33dbe6be35cff5ca2245b6677e Author: James Cherry <[email protected]> Date: Mon Mar 24 09:04:55 2025 -0700 use string Signed-off-by: James Cherry <[email protected]> commit b93a542ddfbcb5c793c9b533cbe64ea20ec08f4a Author: James Cherry <[email protected]> Date: Mon Mar 24 08:59:01 2025 -0700 timingSenseString -> to_string Signed-off-by: James Cherry <[email protected]> commit 6c121a0ff4231b37df076a62e83832897be62ff4 Author: James Cherry <[email protected]> Date: Sun Mar 23 16:09:47 2025 -0700 Corner use string Signed-off-by: James Cherry <[email protected]> commit 07b989a5a43bf5d341aa6ba2880be663997577d5 Author: James Cherry <[email protected]> Date: Sun Mar 23 16:05:43 2025 -0700 Tag::to_string() Signed-off-by: James Cherry <[email protected]> commit 0b9480cc5a3fa9ef0cb1c6e8ba0d4a29de2df816 Author: James Cherry <[email protected]> Date: Sun Mar 23 15:53:29 2025 -0700 PathAnalysisPt::to_string Signed-off-by: James Cherry <[email protected]> commit a028659091e99270f7501615285730681ed59523 Author: James Cherry <[email protected]> Date: Sun Mar 23 12:19:03 2025 -0700 TimingRole stati alloc Signed-off-by: James Cherry <[email protected]> commit 495be6a57bda23d82e511282f5db7c188b32971b Author: James Cherry <[email protected]> Date: Sat Mar 22 21:36:52 2025 -0700 RiseFall/RiseFallBoth/Transition const Signed-off-by: James Cherry <[email protected]> commit 4c4b28adb383321b1172f4b774c7c4d9a1aee69f Author: James Cherry <[email protected]> Date: Sat Mar 22 20:38:26 2025 -0700 TimingRole const Signed-off-by: James Cherry <[email protected]> commit 54ab58ec7200d420bf3b5e709e74b652af88d508 Author: James Cherry <[email protected]> Date: Sat Mar 22 14:15:07 2025 -0700 const MinMax Signed-off-by: James Cherry <[email protected]> commit f70bb38df17b2ed758c7b6ba5647b7355366c0c0 Author: James Cherry <[email protected]> Date: Sat Mar 22 13:14:31 2025 -0700 Transition::to_string(() Signed-off-by: James Cherry <[email protected]> commit b3f3d67328194351fb8efac2219bcfbcec331552 Author: James Cherry <[email protected]> Date: Sat Mar 22 12:33:25 2025 -0700 RiseFall::to_string Signed-off-by: James Cherry <[email protected]> commit 4046f8a376926dfde980860c51d2c5c70cf4a867 Author: James Cherry <[email protected]> Date: Thu Mar 20 09:04:10 2025 -0700 TimingRole::name -> to_string Signed-off-by: James Cherry <[email protected]> commit cf4dd918eccb05d459f1804ced8365c81a5c6a50 Author: James Cherry <[email protected]> Date: Wed Mar 19 20:14:42 2025 -0700 MinMax::asString -> to_string Signed-off-by: James Cherry <[email protected]> commit d80118117dda25be7b2b4896f19e955645c27f73 Author: James Cherry <[email protected]> Date: Wed Mar 19 17:43:08 2025 -0700 TimingRole::name -> to_string Signed-off-by: James Cherry <[email protected]> commit 284fa25c28aca998e8ce92e7b7bb927697494a13 Author: James Cherry <[email protected]> Date: Wed Mar 19 17:02:27 2025 -0700 comment Signed-off-by: James Cherry <[email protected]> commit 646f19749b997e03dc4cbdf165cd7637010276d3 Author: James Cherry <[email protected]> Date: Wed Mar 19 14:47:40 2025 -0700 FuncExpr::asString -> to_string Signed-off-by: James Cherry <[email protected]> commit 4f73d8e7ad21feac6f41130b7b070f3e345b6fb5 Author: James Cherry <[email protected]> Date: Wed Mar 19 14:04:13 2025 -0700 Vertex::name -> to_string Signed-off-by: James Cherry <[email protected]> commit 7c7ec486aaea86f6607a1ef72bb1a74dca603831 Author: James Cherry <[email protected]> Date: Wed Mar 19 13:39:24 2025 -0700 Vertex::name -> to_string Signed-off-by: James Cherry <[email protected]> Signed-off-by: James Cherry <[email protected]>
753 lines
16 KiB
C++
753 lines
16 KiB
C++
// 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 "Clock.hh"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "Error.hh"
|
|
#include "StringUtil.hh"
|
|
#include "MinMax.hh"
|
|
#include "Transition.hh"
|
|
#include "TimingRole.hh"
|
|
#include "Network.hh"
|
|
#include "Graph.hh"
|
|
#include "Sdc.hh"
|
|
|
|
namespace sta {
|
|
|
|
static bool
|
|
isPowerOfTwo(int i);
|
|
|
|
Clock::Clock(const char *name,
|
|
int index,
|
|
const Network *network) :
|
|
name_(stringCopy(name)),
|
|
pins_(network),
|
|
add_to_pins_(false),
|
|
leaf_pins_(network),
|
|
period_(0.0),
|
|
waveform_(nullptr),
|
|
waveform_valid_(false),
|
|
index_(index),
|
|
clk_edges_(nullptr),
|
|
is_propagated_(false),
|
|
uncertainties_(nullptr),
|
|
is_generated_(false),
|
|
src_pin_(nullptr),
|
|
master_clk_(nullptr),
|
|
master_clk_infered_(false),
|
|
divide_by_(0),
|
|
multiply_by_(0),
|
|
duty_cycle_(0),
|
|
invert_(false),
|
|
combinational_(false),
|
|
edges_(nullptr),
|
|
edge_shifts_(nullptr)
|
|
{
|
|
makeClkEdges();
|
|
}
|
|
|
|
void
|
|
Clock::initClk(PinSet *pins,
|
|
bool add_to_pins,
|
|
float period,
|
|
FloatSeq *waveform,
|
|
const char *comment,
|
|
const Network *network)
|
|
{
|
|
is_generated_ = false;
|
|
setPins(pins, network);
|
|
add_to_pins_ = add_to_pins;
|
|
delete waveform_;
|
|
waveform_ = waveform;
|
|
waveform_valid_ = true;
|
|
period_ = period;
|
|
setClkEdgeTimes();
|
|
setComment(comment);
|
|
}
|
|
|
|
bool
|
|
Clock::isVirtual() const
|
|
{
|
|
return pins_.empty();
|
|
}
|
|
|
|
void
|
|
Clock::setPins(PinSet *pins,
|
|
const Network *network)
|
|
{
|
|
if (pins)
|
|
pins_ = *pins;
|
|
delete pins;
|
|
makeLeafPins(network);
|
|
}
|
|
|
|
void
|
|
Clock::makeLeafPins(const Network *network)
|
|
{
|
|
leaf_pins_.clear();
|
|
PinSet::Iterator pin_iter(pins_);
|
|
while (pin_iter.hasNext()) {
|
|
const Pin *pin = pin_iter.next();
|
|
findLeafDriverPins(pin, network, &leaf_pins_);
|
|
}
|
|
}
|
|
|
|
void
|
|
Clock::setMasterClk(Clock *master)
|
|
{
|
|
master_clk_ = master;
|
|
waveform_valid_ = false;
|
|
}
|
|
|
|
void
|
|
Clock::makeClkEdges()
|
|
{
|
|
clk_edges_ = new ClockEdge*[RiseFall::index_count];
|
|
for (auto rf : RiseFall::range()) {
|
|
clk_edges_[rf->index()] = new ClockEdge(this, rf);
|
|
}
|
|
}
|
|
|
|
Clock::~Clock()
|
|
{
|
|
stringDelete(name_);
|
|
if (clk_edges_) {
|
|
delete clk_edges_[RiseFall::riseIndex()];
|
|
delete clk_edges_[RiseFall::fallIndex()];
|
|
delete [] clk_edges_;
|
|
}
|
|
delete waveform_;
|
|
delete edges_;
|
|
delete edge_shifts_;
|
|
delete uncertainties_;
|
|
}
|
|
|
|
void
|
|
Clock::addPin(const Pin *pin)
|
|
{
|
|
pins_.insert(pin);
|
|
leaf_pins_.insert(pin);
|
|
}
|
|
|
|
void
|
|
Clock::deletePin(const Pin *pin)
|
|
{
|
|
pins_.erase(pin);
|
|
}
|
|
|
|
void
|
|
Clock::setAddToPins(bool add_to_pins)
|
|
{
|
|
add_to_pins_ = add_to_pins;
|
|
}
|
|
|
|
void
|
|
Clock::setClkEdgeTimes()
|
|
{
|
|
setClkEdgeTime(RiseFall::rise());
|
|
setClkEdgeTime(RiseFall::fall());
|
|
}
|
|
|
|
void
|
|
Clock::setClkEdgeTime(const RiseFall *rf)
|
|
{
|
|
float time = (rf == RiseFall::rise()) ? (*waveform_)[0]:(*waveform_)[1];
|
|
clk_edges_[rf->index()]->setTime(time);
|
|
}
|
|
|
|
const Pin *
|
|
Clock::defaultPin() const
|
|
{
|
|
PinSet::ConstIterator pin_iter(leaf_pins_);
|
|
if (pin_iter.hasNext())
|
|
return pin_iter.next();
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
ClockEdge *
|
|
Clock::edge(const RiseFall *rf) const
|
|
{
|
|
return clk_edges_[rf->index()];
|
|
}
|
|
|
|
void
|
|
Clock::setIsPropagated(bool propagated)
|
|
{
|
|
is_propagated_ = propagated;
|
|
}
|
|
|
|
void
|
|
Clock::slew(const RiseFall *rf,
|
|
const MinMax *min_max,
|
|
// Return values.
|
|
float &slew,
|
|
bool &exists) const
|
|
{
|
|
slews_.value(rf, min_max, slew, exists);
|
|
}
|
|
|
|
float
|
|
Clock::slew(const RiseFall *rf,
|
|
const MinMax *min_max) const
|
|
{
|
|
float slew;
|
|
bool exists;
|
|
slews_.value(rf, min_max, slew, exists);
|
|
if (!exists)
|
|
slew = 0.0;
|
|
return slew;
|
|
}
|
|
|
|
void
|
|
Clock::setSlew(const RiseFallBoth *rf,
|
|
const MinMaxAll *min_max,
|
|
float slew)
|
|
{
|
|
slews_.setValue(rf, min_max, slew);
|
|
}
|
|
|
|
void
|
|
Clock::setSlew(const RiseFall *rf,
|
|
const MinMax *min_max,
|
|
float slew)
|
|
{
|
|
slews_.setValue(rf, min_max, slew);
|
|
}
|
|
|
|
void
|
|
Clock::removeSlew()
|
|
{
|
|
slews_.clear();
|
|
}
|
|
|
|
void
|
|
Clock::setSlewLimit(const RiseFallBoth *rf,
|
|
const PathClkOrData clk_data,
|
|
const MinMax *min_max,
|
|
float slew)
|
|
{
|
|
slew_limits_[int(clk_data)].setValue(rf, min_max, slew);
|
|
}
|
|
|
|
void
|
|
Clock::slewLimit(const RiseFall *rf,
|
|
const PathClkOrData clk_data,
|
|
const MinMax *min_max,
|
|
// Return values.
|
|
float &slew,
|
|
bool &exists) const
|
|
{
|
|
slew_limits_[int(clk_data)].value(rf, min_max, slew, exists);
|
|
}
|
|
|
|
void
|
|
Clock::uncertainty(const SetupHold *setup_hold,
|
|
// Return values.
|
|
float &uncertainty,
|
|
bool &exists) const
|
|
{
|
|
if (uncertainties_)
|
|
uncertainties_->value(setup_hold, uncertainty, exists);
|
|
else {
|
|
uncertainty = 0.0F;
|
|
exists = false;
|
|
}
|
|
}
|
|
|
|
void
|
|
Clock::setUncertainty(const SetupHoldAll *setup_hold,
|
|
float uncertainty)
|
|
{
|
|
if (uncertainties_ == nullptr)
|
|
uncertainties_ = new ClockUncertainties;
|
|
uncertainties_->setValue(setup_hold, uncertainty);
|
|
}
|
|
|
|
void
|
|
Clock::setUncertainty(const SetupHold *setup_hold,
|
|
float uncertainty)
|
|
{
|
|
if (uncertainties_ == nullptr)
|
|
uncertainties_ = new ClockUncertainties;
|
|
uncertainties_->setValue(setup_hold, uncertainty);
|
|
}
|
|
|
|
void
|
|
Clock::removeUncertainty(const SetupHoldAll *setup_hold)
|
|
{
|
|
if (uncertainties_) {
|
|
uncertainties_->removeValue(setup_hold);
|
|
if (uncertainties_->empty()) {
|
|
delete uncertainties_;
|
|
uncertainties_ = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Clock::waveformInvalid()
|
|
{
|
|
waveform_valid_ = false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
Clock::initGeneratedClk(PinSet *pins,
|
|
bool add_to_pins,
|
|
Pin *src_pin,
|
|
Clock *master_clk,
|
|
int divide_by,
|
|
int multiply_by,
|
|
float duty_cycle,
|
|
bool invert,
|
|
bool combinational,
|
|
IntSeq *edges,
|
|
FloatSeq *edge_shifts,
|
|
bool is_propagated,
|
|
const char *comment,
|
|
const Network *network)
|
|
{
|
|
is_generated_ = true;
|
|
setPins(pins, network);
|
|
add_to_pins_ = add_to_pins;
|
|
src_pin_ = src_pin;
|
|
master_clk_ = master_clk;
|
|
master_clk_infered_ = false;
|
|
waveform_valid_ = false;
|
|
divide_by_ = divide_by;
|
|
multiply_by_ = multiply_by;
|
|
duty_cycle_ = duty_cycle;
|
|
invert_ = invert;
|
|
combinational_ = combinational;
|
|
is_propagated_ = is_propagated;
|
|
setComment(comment);
|
|
|
|
delete edges_;
|
|
if (edges
|
|
&& edges->empty()) {
|
|
delete edges;
|
|
edges = nullptr;
|
|
}
|
|
edges_ = edges;
|
|
|
|
delete edge_shifts_;
|
|
if (edge_shifts
|
|
&& edge_shifts->empty()) {
|
|
delete edge_shifts;
|
|
edge_shifts = nullptr;
|
|
}
|
|
edge_shifts_ = edge_shifts;
|
|
}
|
|
|
|
void
|
|
Clock::setInferedMasterClk(Clock *master_clk)
|
|
{
|
|
master_clk_ = master_clk;
|
|
master_clk_infered_ = true;
|
|
waveform_valid_ = false;
|
|
}
|
|
|
|
bool
|
|
Clock::isGenerated() const
|
|
{
|
|
return is_generated_;
|
|
}
|
|
|
|
bool
|
|
Clock::isGeneratedWithPropagatedMaster() const
|
|
{
|
|
return is_generated_
|
|
&& master_clk_
|
|
// Insertion is zero if the master clock is ideal.
|
|
&& master_clk_->isPropagated();
|
|
}
|
|
|
|
void
|
|
Clock::generate(const Clock *src_clk)
|
|
{
|
|
if (waveform_ == nullptr)
|
|
waveform_ = new FloatSeq;
|
|
else
|
|
waveform_->clear();
|
|
|
|
if (divide_by_ == 1.0) {
|
|
period_ = src_clk->period();
|
|
const FloatSeq *src_wave = src_clk->waveform();
|
|
waveform_->push_back((*src_wave)[0]);
|
|
waveform_->push_back((*src_wave)[1]);
|
|
}
|
|
else if (divide_by_ > 1) {
|
|
if (isPowerOfTwo(divide_by_)) {
|
|
period_ = src_clk->period() * divide_by_;
|
|
const FloatSeq *src_wave = src_clk->waveform();
|
|
float rise = (*src_wave)[0];
|
|
waveform_->push_back(rise);
|
|
waveform_->push_back(rise + period_ / 2);
|
|
}
|
|
else
|
|
generateScaledClk(src_clk, static_cast<float>(divide_by_));
|
|
}
|
|
else if (multiply_by_ >= 1)
|
|
generateScaledClk(src_clk, 1.0F / multiply_by_);
|
|
else if (edges_)
|
|
generateEdgesClk(src_clk);
|
|
|
|
if (invert_) {
|
|
float first_time = (*waveform_)[0];
|
|
float offset = (first_time >= period_) ? period_ : 0.0F;
|
|
size_t edge_count = waveform_->size();
|
|
for (size_t i = 0; i < edge_count - 1; i++)
|
|
(*waveform_)[i] = (*waveform_)[i + 1] - offset;
|
|
(*waveform_)[edge_count - 1] = first_time - offset + period_;
|
|
}
|
|
setClkEdgeTimes();
|
|
waveform_valid_ = true;
|
|
}
|
|
|
|
void
|
|
Clock::generateScaledClk(const Clock *src_clk,
|
|
float scale)
|
|
{
|
|
period_ = src_clk->period() * scale;
|
|
if (duty_cycle_ != 0.0) {
|
|
float rise = (*src_clk->waveform())[0] * scale;
|
|
waveform_->push_back(rise);
|
|
waveform_->push_back(rise + period_ * duty_cycle_ / 100.0F);
|
|
}
|
|
else {
|
|
FloatSeq::ConstIterator wave_iter(src_clk->waveform());
|
|
while (wave_iter.hasNext()) {
|
|
float time = wave_iter.next();
|
|
waveform_->push_back(time * scale);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Clock::generateEdgesClk(const Clock *src_clk)
|
|
{
|
|
// The create_generated_clock tcl cmd and Sta::makeClock
|
|
// enforce this restriction.
|
|
if (edges_->size() == 3) {
|
|
const FloatSeq *src_wave = src_clk->waveform();
|
|
size_t src_size = src_wave->size();
|
|
float src_period = src_clk->period();
|
|
|
|
int edge0_1 = (*edges_)[0] - 1;
|
|
float rise = (*src_wave)[edge0_1 % src_size]
|
|
+ (edge0_1 / src_size) * src_period;
|
|
if (edge_shifts_)
|
|
rise += (*edge_shifts_)[0];
|
|
waveform_->push_back(rise);
|
|
|
|
int edge1_1 = (*edges_)[1] - 1;
|
|
float fall = (*src_wave)[edge1_1 % src_size]
|
|
+ (edge1_1 / src_size) * src_period;
|
|
if (edge_shifts_)
|
|
fall += (*edge_shifts_)[1];
|
|
waveform_->push_back(fall);
|
|
|
|
int edge2_1 = (*edges_)[2] - 1;
|
|
period_ = (*src_wave)[edge2_1 % src_size]
|
|
+ (edge2_1 / src_size) * src_period - rise;
|
|
if (edge_shifts_)
|
|
period_ += (*edge_shifts_)[2];
|
|
}
|
|
else
|
|
criticalError(244, "generated clock edges size is not three.");
|
|
}
|
|
|
|
static bool
|
|
isPowerOfTwo(int i)
|
|
{
|
|
return (i & (i - 1)) == 0;
|
|
}
|
|
|
|
const RiseFall *
|
|
Clock::masterClkEdgeTr(const RiseFall *rf) const
|
|
{
|
|
int edge_index = (rf == RiseFall::rise()) ? 0 : 1;
|
|
return ((*edges_)[edge_index] - 1) % 2
|
|
? RiseFall::fall()
|
|
: RiseFall::rise();
|
|
}
|
|
|
|
void
|
|
Clock::srcPinVertices(VertexSet &src_vertices,
|
|
const Network *network,
|
|
Graph *graph)
|
|
{
|
|
if (network->isHierarchical(src_pin_)) {
|
|
// Use the clocks on a non-hierarchical pin on the same net.
|
|
PinSet leaf_pins(network);
|
|
findLeafDriverPins(src_pin_, network, &leaf_pins);
|
|
PinSet::Iterator pin_iter(leaf_pins);
|
|
while (pin_iter.hasNext()) {
|
|
const Pin *pin = pin_iter.next();
|
|
Vertex *vertex, *bidirect_drvr_vertex;
|
|
graph->pinVertices(pin, vertex, bidirect_drvr_vertex);
|
|
if (vertex)
|
|
src_vertices.insert(vertex);
|
|
if (bidirect_drvr_vertex)
|
|
src_vertices.insert(bidirect_drvr_vertex);
|
|
}
|
|
}
|
|
else {
|
|
Vertex *vertex = graph->pinDrvrVertex(src_pin_);
|
|
src_vertices.insert(vertex);
|
|
}
|
|
}
|
|
|
|
bool
|
|
Clock::isDivideByOneCombinational() const
|
|
{
|
|
return combinational_
|
|
&& divide_by_ == 1
|
|
&& multiply_by_ == 0
|
|
&& edge_shifts_ == 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
ClockEdge::ClockEdge(Clock *clock,
|
|
const RiseFall *rf) :
|
|
clock_(clock),
|
|
rf_(rf),
|
|
name_(stringPrint("%s %s", clock_->name(), rf_->to_string().c_str())),
|
|
time_(0.0),
|
|
index_(clock_->index() * RiseFall::index_count + rf_->index())
|
|
{
|
|
}
|
|
|
|
ClockEdge::~ClockEdge()
|
|
{
|
|
stringDelete(name_);
|
|
}
|
|
|
|
void
|
|
ClockEdge::setTime(float time)
|
|
{
|
|
time_ = time;
|
|
}
|
|
|
|
ClockEdge *
|
|
ClockEdge::opposite() const
|
|
{
|
|
return clock_->edge(rf_->opposite());
|
|
}
|
|
|
|
float
|
|
ClockEdge::pulseWidth() const
|
|
{
|
|
ClockEdge *opp_clk_edge = opposite();
|
|
float width = opp_clk_edge->time() - time_;
|
|
if (width < 0.0)
|
|
width += clock_->period();
|
|
return width;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
int
|
|
clkCmp(const Clock *clk1,
|
|
const Clock *clk2)
|
|
{
|
|
if (clk1 == nullptr && clk2)
|
|
return -1;
|
|
else if (clk1 == nullptr && clk2 == nullptr)
|
|
return 0;
|
|
else if (clk1 && clk2 == nullptr)
|
|
return 1;
|
|
else {
|
|
int index1 = clk1->index();
|
|
int index2 = clk2->index();
|
|
if (index1 < index2)
|
|
return -1;
|
|
else if (index1 == index2)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int
|
|
clkEdgeCmp(const ClockEdge *clk_edge1,
|
|
const ClockEdge *clk_edge2)
|
|
{
|
|
if (clk_edge1 == nullptr && clk_edge2)
|
|
return -1;
|
|
else if (clk_edge1 == nullptr && clk_edge2 == nullptr)
|
|
return 0;
|
|
else if (clk_edge1 && clk_edge2 == nullptr)
|
|
return 1;
|
|
else {
|
|
int index1 = clk_edge1->index();
|
|
int index2 = clk_edge2->index();
|
|
if (index1 == index2)
|
|
return 0;
|
|
else if (index1 < index2)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
bool
|
|
clkEdgeLess(const ClockEdge *clk_edge1,
|
|
const ClockEdge *clk_edge2)
|
|
{
|
|
return clkEdgeCmp(clk_edge1, clk_edge2) < 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
InterClockUncertainty::InterClockUncertainty(const Clock *src,
|
|
const Clock *target) :
|
|
src_(src),
|
|
target_(target)
|
|
{
|
|
}
|
|
|
|
bool
|
|
InterClockUncertainty::empty() const
|
|
{
|
|
return uncertainties_[RiseFall::riseIndex()].empty()
|
|
&& uncertainties_[RiseFall::fallIndex()].empty();
|
|
}
|
|
|
|
void
|
|
InterClockUncertainty::uncertainty(const RiseFall *src_rf,
|
|
const RiseFall *tgt_rf,
|
|
const SetupHold *setup_hold,
|
|
float &uncertainty,
|
|
bool &exists) const
|
|
{
|
|
uncertainties_[src_rf->index()].value(tgt_rf, setup_hold,
|
|
uncertainty, exists);
|
|
}
|
|
|
|
void
|
|
InterClockUncertainty::setUncertainty(const RiseFallBoth *src_rf,
|
|
const RiseFallBoth *tgt_rf,
|
|
const SetupHoldAll *setup_hold,
|
|
float uncertainty)
|
|
{
|
|
for (auto src_rf_index : src_rf->rangeIndex())
|
|
uncertainties_[src_rf_index].setValue(tgt_rf, setup_hold, uncertainty);
|
|
}
|
|
|
|
void
|
|
InterClockUncertainty::removeUncertainty(const RiseFallBoth *src_rf,
|
|
const RiseFallBoth *tgt_rf,
|
|
const SetupHoldAll *setup_hold)
|
|
{
|
|
for (auto src_rf_index : src_rf->rangeIndex())
|
|
uncertainties_[src_rf_index].removeValue(tgt_rf, setup_hold);
|
|
}
|
|
|
|
const RiseFallMinMax *
|
|
InterClockUncertainty::uncertainties(const RiseFall *src_rf) const
|
|
{
|
|
return &uncertainties_[src_rf->index()];
|
|
}
|
|
|
|
bool
|
|
InterClockUncertaintyLess::operator()(const InterClockUncertainty *inter1,
|
|
const InterClockUncertainty *inter2)const
|
|
{
|
|
return inter1->src()->index() < inter2->src()->index()
|
|
|| (inter1->src() == inter2->src()
|
|
&& inter1->target()->index() < inter2->target()->index());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
bool
|
|
ClockNameLess::operator()(const Clock *clk1,
|
|
const Clock *clk2)
|
|
{
|
|
return stringLess(clk1->name(), clk2->name());
|
|
}
|
|
|
|
|
|
bool
|
|
ClockIndexLess::operator()(const Clock *clk1,
|
|
const Clock *clk2) const
|
|
{
|
|
return (clk1 == nullptr && clk2)
|
|
|| (clk1 && clk2
|
|
&& clk1->index() < clk2->index());
|
|
}
|
|
|
|
ClockSeq
|
|
sortByName(ClockSet *set)
|
|
{
|
|
ClockSeq clks;
|
|
for (Clock *clk : *set)
|
|
clks.push_back(clk);
|
|
sort(clks, ClockNameLess());
|
|
return clks;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
bool
|
|
ClockSetLess::operator()(const ClockSet *set1,
|
|
const ClockSet *set2) const
|
|
{
|
|
return sta::compare(set1, set2) < 0;
|
|
}
|
|
|
|
int
|
|
compare(const ClockSet *set1,
|
|
const ClockSet *set2)
|
|
{
|
|
size_t size1 = set1 ? set1->size() : 0;
|
|
size_t size2 = set2 ? set2->size() : 0;
|
|
if (size1 == size2) {
|
|
ClockSet::ConstIterator iter1(set1);
|
|
ClockSet::ConstIterator iter2(set2);
|
|
while (iter1.hasNext() && iter2.hasNext()) {
|
|
Clock *clk1 = iter1.next();
|
|
Clock *clk2 = iter2.next();
|
|
int id1 = clk1->index();
|
|
int id2 = clk2->index();
|
|
if (id1 < id2)
|
|
return -1;
|
|
else if (id1 > id2)
|
|
return 1;
|
|
}
|
|
// Sets are equal.
|
|
return 0;
|
|
}
|
|
else
|
|
return (size1 > size2) ? 1 : -1;
|
|
}
|
|
|
|
} // namespace
|