2018-09-28 17:54:21 +02:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2025-01-22 02:54:33 +01:00
|
|
|
// Copyright (c) 2025, Parallax Software, Inc.
|
2018-09-28 17:54:21 +02:00
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2022-01-04 18:17:08 +01:00
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2018-09-28 17:54:21 +02:00
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
2022-01-04 18:17:08 +01:00
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2025-01-22 02:54:33 +01:00
|
|
|
//
|
|
|
|
|
// 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.
|
2018-09-28 17:54:21 +02:00
|
|
|
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "WriteSdc.hh"
|
2020-04-05 20:35:51 +02:00
|
|
|
|
2023-06-15 17:59:56 +02:00
|
|
|
#include <cstdio>
|
2018-09-28 17:54:21 +02:00
|
|
|
#include <algorithm>
|
2023-06-15 17:59:56 +02:00
|
|
|
#include <ctime>
|
2020-04-05 20:35:51 +02:00
|
|
|
|
2021-11-15 15:31:29 +01:00
|
|
|
#include "Zlib.hh"
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "Report.hh"
|
|
|
|
|
#include "Error.hh"
|
|
|
|
|
#include "Units.hh"
|
|
|
|
|
#include "Transition.hh"
|
|
|
|
|
#include "Liberty.hh"
|
|
|
|
|
#include "Wireload.hh"
|
|
|
|
|
#include "Network.hh"
|
|
|
|
|
#include "PortDirection.hh"
|
|
|
|
|
#include "NetworkCmp.hh"
|
|
|
|
|
#include "Graph.hh"
|
|
|
|
|
#include "GraphCmp.hh"
|
|
|
|
|
#include "RiseFallValues.hh"
|
|
|
|
|
#include "PortDelay.hh"
|
|
|
|
|
#include "ExceptionPath.hh"
|
|
|
|
|
#include "PortExtCap.hh"
|
|
|
|
|
#include "DisabledPorts.hh"
|
|
|
|
|
#include "ClockGroups.hh"
|
|
|
|
|
#include "ClockInsertion.hh"
|
|
|
|
|
#include "ClockLatency.hh"
|
|
|
|
|
#include "InputDrive.hh"
|
|
|
|
|
#include "DataCheck.hh"
|
|
|
|
|
#include "DeratingFactors.hh"
|
|
|
|
|
#include "Sdc.hh"
|
2021-05-06 08:12:53 +02:00
|
|
|
#include "Fuzzy.hh"
|
2020-04-05 23:53:44 +02:00
|
|
|
#include "StaState.hh"
|
2023-01-19 19:23:45 +01:00
|
|
|
#include "Corner.hh"
|
2025-04-10 01:35:15 +02:00
|
|
|
#include "Variables.hh"
|
2021-12-07 00:36:01 +01:00
|
|
|
#include "WriteSdcPvt.hh"
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
|
|
|
|
typedef Set<ClockSense*> ClockSenseSet;
|
|
|
|
|
typedef Vector<ClockSense*> ClockSenseSeq;
|
|
|
|
|
|
|
|
|
|
static const char *
|
2019-11-11 23:30:19 +01:00
|
|
|
transRiseFallFlag(const RiseFall *rf);
|
2018-09-28 17:54:21 +02:00
|
|
|
static const char *
|
2019-11-11 23:30:19 +01:00
|
|
|
transRiseFallFlag(const RiseFallBoth *rf);
|
2018-09-28 17:54:21 +02:00
|
|
|
static const char *
|
|
|
|
|
minMaxFlag(const MinMaxAll *min_max);
|
|
|
|
|
static const char *
|
|
|
|
|
minMaxFlag(const MinMax *min_max);
|
|
|
|
|
static const char *
|
|
|
|
|
earlyLateFlag(const MinMax *early_late);
|
|
|
|
|
static const char *
|
|
|
|
|
setupHoldFlag(const MinMax *min_max);
|
|
|
|
|
static const char *
|
|
|
|
|
timingDerateTypeKeyword(TimingDerateType type);
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
class WriteSdcObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WriteSdcObject() {}
|
|
|
|
|
virtual ~WriteSdcObject() {}
|
|
|
|
|
virtual void write() const = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class WriteGetPort : public WriteSdcObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WriteGetPort(const Port *port,
|
|
|
|
|
const WriteSdc *writer);
|
|
|
|
|
virtual void write() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const Port *port_;
|
|
|
|
|
const WriteSdc *writer_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
WriteGetPort::WriteGetPort(const Port *port,
|
2021-11-15 15:31:29 +01:00
|
|
|
const WriteSdc *writer) :
|
2018-09-28 17:54:21 +02:00
|
|
|
port_(port),
|
|
|
|
|
writer_(writer)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteGetPort::write() const
|
|
|
|
|
{
|
|
|
|
|
writer_->writeGetPort(port_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class WriteGetPinAndClkKey : public WriteSdcObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WriteGetPinAndClkKey(const Pin *pin,
|
2019-10-25 17:51:59 +02:00
|
|
|
bool map_hpin_to_drvr,
|
2018-09-28 17:54:21 +02:00
|
|
|
const Clock *clk,
|
|
|
|
|
const WriteSdc *writer);
|
|
|
|
|
virtual void write() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const Pin *pin_;
|
2019-10-25 17:51:59 +02:00
|
|
|
bool map_hpin_to_drvr_;
|
2018-09-28 17:54:21 +02:00
|
|
|
const Clock *clk_;
|
|
|
|
|
const WriteSdc *writer_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
WriteGetPinAndClkKey::WriteGetPinAndClkKey(const Pin *pin,
|
2019-10-25 17:51:59 +02:00
|
|
|
bool map_hpin_to_drvr,
|
2018-09-28 17:54:21 +02:00
|
|
|
const Clock *clk,
|
|
|
|
|
const WriteSdc *writer) :
|
|
|
|
|
pin_(pin),
|
2019-10-25 17:51:59 +02:00
|
|
|
map_hpin_to_drvr_(map_hpin_to_drvr),
|
2018-09-28 17:54:21 +02:00
|
|
|
clk_(clk),
|
|
|
|
|
writer_(writer)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteGetPinAndClkKey::write() const
|
|
|
|
|
{
|
|
|
|
|
writer_->writeClockKey(clk_);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(writer_->stream(), " ");
|
2019-10-25 17:51:59 +02:00
|
|
|
writer_->writeGetPin(pin_, map_hpin_to_drvr_);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class WriteGetPin : public WriteSdcObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WriteGetPin(const Pin *pin,
|
2019-10-25 17:51:59 +02:00
|
|
|
bool map_hpin_to_drvr,
|
2018-09-28 17:54:21 +02:00
|
|
|
const WriteSdc *writer);
|
|
|
|
|
virtual void write() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const Pin *pin_;
|
2019-10-25 17:51:59 +02:00
|
|
|
bool map_hpin_to_drvr_;
|
2018-09-28 17:54:21 +02:00
|
|
|
const WriteSdc *writer_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
WriteGetPin::WriteGetPin(const Pin *pin,
|
2019-10-25 17:51:59 +02:00
|
|
|
bool map_hpin_to_drvr,
|
2021-11-15 15:31:29 +01:00
|
|
|
const WriteSdc *writer) :
|
2018-09-28 17:54:21 +02:00
|
|
|
pin_(pin),
|
2019-10-25 17:51:59 +02:00
|
|
|
map_hpin_to_drvr_(map_hpin_to_drvr),
|
2018-09-28 17:54:21 +02:00
|
|
|
writer_(writer)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteGetPin::write() const
|
|
|
|
|
{
|
2019-10-25 17:51:59 +02:00
|
|
|
writer_->writeGetPin(pin_, map_hpin_to_drvr_);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class WriteGetNet : public WriteSdcObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WriteGetNet(const Net *net,
|
|
|
|
|
const WriteSdc *writer);
|
|
|
|
|
virtual void write() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const Net *net_;
|
|
|
|
|
const WriteSdc *writer_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
WriteGetNet::WriteGetNet(const Net *net,
|
|
|
|
|
const WriteSdc *writer) :
|
|
|
|
|
net_(net),
|
|
|
|
|
writer_(writer)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteGetNet::write() const
|
|
|
|
|
{
|
|
|
|
|
writer_->writeGetNet(net_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class WriteGetInstance : public WriteSdcObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WriteGetInstance(const Instance *inst,
|
|
|
|
|
const WriteSdc *writer);
|
|
|
|
|
virtual void write() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const Instance *inst_;
|
|
|
|
|
const WriteSdc *writer_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
WriteGetInstance::WriteGetInstance(const Instance *inst,
|
|
|
|
|
const WriteSdc *writer) :
|
|
|
|
|
inst_(inst),
|
|
|
|
|
writer_(writer)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteGetInstance::write() const
|
|
|
|
|
{
|
|
|
|
|
writer_->writeGetInstance(inst_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class WriteGetLibCell : public WriteSdcObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WriteGetLibCell(const LibertyCell *cell,
|
|
|
|
|
const WriteSdc *writer);
|
|
|
|
|
virtual void write() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const LibertyCell *cell_;
|
|
|
|
|
const WriteSdc *writer_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
WriteGetLibCell::WriteGetLibCell(const LibertyCell *cell,
|
|
|
|
|
const WriteSdc *writer) :
|
|
|
|
|
cell_(cell),
|
|
|
|
|
writer_(writer)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteGetLibCell::write() const
|
|
|
|
|
{
|
|
|
|
|
writer_->writeGetLibCell(cell_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class WriteGetClock : public WriteSdcObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WriteGetClock(const Clock *clk,
|
|
|
|
|
const WriteSdc *writer);
|
|
|
|
|
virtual void write() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const Clock *clk_;
|
|
|
|
|
const WriteSdc *writer_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
WriteGetClock::WriteGetClock(const Clock *clk,
|
|
|
|
|
const WriteSdc *writer) :
|
|
|
|
|
clk_(clk),
|
|
|
|
|
writer_(writer)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteGetClock::write() const
|
|
|
|
|
{
|
|
|
|
|
writer_->writeGetClock(clk_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
writeSdc(Instance *instance,
|
|
|
|
|
const char *filename,
|
|
|
|
|
const char *creator,
|
2019-10-25 17:51:59 +02:00
|
|
|
bool map_hpins,
|
|
|
|
|
bool native,
|
2018-09-28 17:54:21 +02:00
|
|
|
int digits,
|
2021-11-15 15:31:29 +01:00
|
|
|
bool gzip,
|
|
|
|
|
bool no_timestamp,
|
2018-09-28 17:54:21 +02:00
|
|
|
Sdc *sdc)
|
|
|
|
|
{
|
2021-11-15 15:36:41 +01:00
|
|
|
WriteSdc writer(instance, creator, map_hpins, native,
|
|
|
|
|
digits, no_timestamp, sdc);
|
|
|
|
|
writer.write(filename, gzip);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WriteSdc::WriteSdc(Instance *instance,
|
|
|
|
|
const char *creator,
|
2019-10-25 17:51:59 +02:00
|
|
|
bool map_hpins,
|
|
|
|
|
bool native,
|
2018-09-28 17:54:21 +02:00
|
|
|
int digits,
|
|
|
|
|
bool no_timestamp,
|
|
|
|
|
Sdc *sdc) :
|
|
|
|
|
StaState(sdc),
|
|
|
|
|
instance_(instance),
|
|
|
|
|
creator_(creator),
|
2019-10-25 17:51:59 +02:00
|
|
|
map_hpins_(map_hpins),
|
|
|
|
|
native_(native),
|
2018-09-28 17:54:21 +02:00
|
|
|
digits_(digits),
|
|
|
|
|
no_timestamp_(no_timestamp),
|
|
|
|
|
top_instance_(instance == sdc_network_->topInstance()),
|
|
|
|
|
instance_name_length_(strlen(sdc_network_->pathName(instance))),
|
|
|
|
|
cell_(sdc_network_->cell(instance))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WriteSdc::~WriteSdc()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2021-11-15 15:36:41 +01:00
|
|
|
WriteSdc::write(const char *filename,
|
|
|
|
|
bool gzip)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2021-11-15 15:36:41 +01:00
|
|
|
openFile(filename, gzip);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeHeader();
|
|
|
|
|
writeTiming();
|
|
|
|
|
writeEnvironment();
|
|
|
|
|
writeDesignRules();
|
|
|
|
|
writeVariables();
|
|
|
|
|
closeFile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2021-11-15 15:36:41 +01:00
|
|
|
WriteSdc::openFile(const char *filename,
|
|
|
|
|
bool gzip)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2021-11-15 15:36:41 +01:00
|
|
|
stream_ = gzopen(filename, gzip ? "wb" : "wT");
|
2019-03-13 01:25:53 +01:00
|
|
|
if (stream_ == nullptr)
|
2021-11-15 15:36:41 +01:00
|
|
|
throw FileNotWritable(filename);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::closeFile()
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzclose(stream_);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeHeader() const
|
|
|
|
|
{
|
|
|
|
|
writeCommentSeparator();
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "# Created by %s\n", creator_);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!no_timestamp_) {
|
|
|
|
|
time_t now;
|
|
|
|
|
time(&now);
|
|
|
|
|
char *time_str = ctime(&now);
|
|
|
|
|
// Remove trailing \n.
|
|
|
|
|
time_str[strlen(time_str) - 1] = '\0';
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "# %s\n", time_str);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
writeCommentSeparator();
|
|
|
|
|
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "current_design %s\n", sdc_network_->name(cell_));
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeTiming() const
|
|
|
|
|
{
|
|
|
|
|
writeCommentSection("Timing Constraints");
|
|
|
|
|
writeClocks();
|
|
|
|
|
writePropagatedClkPins();
|
|
|
|
|
writeClockUncertaintyPins();
|
|
|
|
|
writeClockLatencies();
|
|
|
|
|
writeClockInsertions();
|
|
|
|
|
writeInterClockUncertainties();
|
|
|
|
|
writeClockSenses();
|
|
|
|
|
writeClockGroups();
|
|
|
|
|
writeInputDelays();
|
|
|
|
|
writeOutputDelays();
|
|
|
|
|
writeDisables();
|
|
|
|
|
writeExceptions();
|
|
|
|
|
writeDataChecks();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClocks() const
|
|
|
|
|
{
|
|
|
|
|
// Write clocks in the order they were defined because generated
|
|
|
|
|
// clocks depend on master clocks having been previously defined.
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto clk : sdc_->clocks_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (clk->isGenerated())
|
|
|
|
|
writeGeneratedClock(clk);
|
|
|
|
|
else
|
|
|
|
|
writeClock(clk);
|
|
|
|
|
writeClockSlews(clk);
|
|
|
|
|
writeClockUncertainty(clk);
|
|
|
|
|
if (clk->isPropagated()) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_propagated_clock ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetClock(clk);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClock(Clock *clk) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "create_clock -name %s",
|
2018-09-28 17:54:21 +02:00
|
|
|
clk->name());
|
|
|
|
|
if (clk->addToPins())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -add");
|
|
|
|
|
gzprintf(stream_, " -period ");
|
2021-05-06 08:12:53 +02:00
|
|
|
float period = clk->period();
|
|
|
|
|
writeTime(period);
|
|
|
|
|
FloatSeq *waveform = clk->waveform();
|
|
|
|
|
if (!(waveform->size() == 2
|
|
|
|
|
&& (*waveform)[0] == 0.0
|
|
|
|
|
&& fuzzyEqual((*waveform)[1], period / 2.0))) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -waveform ");
|
2021-05-06 08:12:53 +02:00
|
|
|
writeFloatSeq(waveform, scaleTime(1.0));
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
writeCmdComment(clk);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeClockPins(clk);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGeneratedClock(Clock *clk) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "create_generated_clock -name %s",
|
2018-09-28 17:54:21 +02:00
|
|
|
clk->name());
|
|
|
|
|
if (clk->addToPins())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -add");
|
|
|
|
|
gzprintf(stream_, " -source ");
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(clk->srcPin(), true);
|
2018-09-28 17:54:21 +02:00
|
|
|
Clock *master = clk->masterClk();
|
|
|
|
|
if (master && !clk->masterClkInfered()) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -master_clock ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetClock(master);
|
|
|
|
|
}
|
|
|
|
|
if (clk->combinational())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -combinational");
|
2018-09-28 17:54:21 +02:00
|
|
|
int divide_by = clk->divideBy();
|
|
|
|
|
if (divide_by != 0)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -divide_by %d", divide_by);
|
2018-09-28 17:54:21 +02:00
|
|
|
int multiply_by = clk->multiplyBy();
|
|
|
|
|
if (multiply_by != 0)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -multiply_by %d", multiply_by);
|
2018-09-28 17:54:21 +02:00
|
|
|
float duty_cycle = clk->dutyCycle();
|
|
|
|
|
if (duty_cycle != 0.0) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -duty_cycle ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeFloat(duty_cycle);
|
|
|
|
|
}
|
|
|
|
|
if (clk->invert())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -invert");
|
2018-09-28 17:54:21 +02:00
|
|
|
IntSeq *edges = clk->edges();
|
|
|
|
|
if (edges && !edges->empty()) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -edges ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeIntSeq(edges);
|
|
|
|
|
FloatSeq *edge_shifts = clk->edgeShifts();
|
|
|
|
|
if (edge_shifts && !edge_shifts->empty()) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -edge_shift ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeFloatSeq(edge_shifts, scaleTime(1.0));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
writeCmdComment(clk);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeClockPins(clk);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::writeClockPins(const Clock *clk) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
const PinSet &pins = clk->pins();
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!pins.empty()) {
|
|
|
|
|
if (pins.size() > 1)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\\\n ");
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPins(&pins, true);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::writeClockSlews(const Clock *clk) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
WriteGetClock write_clk(clk, this);
|
2023-01-19 19:23:45 +01:00
|
|
|
const RiseFallMinMax slews = clk->slews();
|
|
|
|
|
if (slews.hasValue())
|
|
|
|
|
writeRiseFallMinMaxTimeCmd("set_clock_transition", &slews, write_clk);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::writeClockUncertainty(const Clock *clk) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
float setup;
|
|
|
|
|
bool setup_exists;
|
|
|
|
|
clk->uncertainty(SetupHold::max(), setup, setup_exists);
|
|
|
|
|
float hold;
|
|
|
|
|
bool hold_exists;
|
|
|
|
|
clk->uncertainty(SetupHold::min(), hold, hold_exists);
|
|
|
|
|
if (setup_exists && hold_exists && setup == hold)
|
|
|
|
|
writeClockUncertainty(clk, "", setup);
|
|
|
|
|
else {
|
|
|
|
|
if (setup_exists)
|
|
|
|
|
writeClockUncertainty(clk, "-setup ", setup);
|
|
|
|
|
if (hold_exists)
|
|
|
|
|
writeClockUncertainty(clk, "-hold ", hold);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::writeClockUncertainty(const Clock *clk,
|
2018-09-28 17:54:21 +02:00
|
|
|
const char *setup_hold,
|
|
|
|
|
float value) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_clock_uncertainty %s", setup_hold);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeTime(value);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " %s\n", clk->name());
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockUncertaintyPins() const
|
|
|
|
|
{
|
|
|
|
|
PinClockUncertaintyMap::Iterator iter(sdc_->pin_clk_uncertainty_map_);
|
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
|
const Pin *pin;
|
|
|
|
|
ClockUncertainties *uncertainties;
|
|
|
|
|
iter.next(pin, uncertainties);
|
|
|
|
|
writeClockUncertaintyPin(pin, uncertainties);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockUncertaintyPin(const Pin *pin,
|
|
|
|
|
ClockUncertainties *uncertainties)
|
|
|
|
|
const
|
|
|
|
|
{
|
|
|
|
|
float setup;
|
|
|
|
|
bool setup_exists;
|
|
|
|
|
uncertainties->value(SetupHold::max(), setup, setup_exists);
|
|
|
|
|
float hold;
|
|
|
|
|
bool hold_exists;
|
|
|
|
|
uncertainties->value(SetupHold::min(), hold, hold_exists);
|
|
|
|
|
if (setup_exists && hold_exists && setup == hold)
|
|
|
|
|
writeClockUncertaintyPin(pin, "", setup);
|
|
|
|
|
else {
|
|
|
|
|
if (setup_exists)
|
|
|
|
|
writeClockUncertaintyPin(pin, "-setup ", setup);
|
|
|
|
|
if (hold_exists)
|
|
|
|
|
writeClockUncertaintyPin(pin, "-hold ", hold);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockUncertaintyPin(const Pin *pin,
|
|
|
|
|
const char *setup_hold,
|
|
|
|
|
float value) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_clock_uncertainty %s", setup_hold);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeTime(value);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(pin, true);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockLatencies() const
|
|
|
|
|
{
|
|
|
|
|
ClockLatencies::Iterator latency_iter(sdc_->clockLatencies());
|
|
|
|
|
while (latency_iter.hasNext()) {
|
|
|
|
|
ClockLatency *latency = latency_iter.next();
|
|
|
|
|
const Pin *pin = latency->pin();
|
|
|
|
|
const Clock *clk = latency->clock();
|
|
|
|
|
if (pin && clk) {
|
2019-10-25 17:51:59 +02:00
|
|
|
WriteGetPinAndClkKey write_pin(pin, true, clk, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeRiseFallMinMaxTimeCmd("set_clock_latency", latency->delays(),
|
|
|
|
|
write_pin);
|
|
|
|
|
}
|
|
|
|
|
else if (pin) {
|
2019-10-25 17:51:59 +02:00
|
|
|
WriteGetPin write_pin(pin, true, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeRiseFallMinMaxTimeCmd("set_clock_latency", latency->delays(),
|
|
|
|
|
write_pin);
|
|
|
|
|
}
|
|
|
|
|
else if (clk) {
|
|
|
|
|
WriteGetClock write_clk(clk, this);
|
|
|
|
|
writeRiseFallMinMaxTimeCmd("set_clock_latency", latency->delays(),
|
|
|
|
|
write_clk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockInsertions() const
|
|
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
for (ClockInsertion *insert : sdc_->clockInsertions()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
const Pin *pin = insert->pin();
|
|
|
|
|
const Clock *clk = insert->clock();
|
|
|
|
|
if (pin && clk) {
|
2019-10-25 17:51:59 +02:00
|
|
|
WriteGetPinAndClkKey write_pin_clk(pin, true, clk, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeClockInsertion(insert, write_pin_clk);
|
|
|
|
|
}
|
|
|
|
|
else if (pin) {
|
2019-10-25 17:51:59 +02:00
|
|
|
WriteGetPin write_pin(pin, true, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeClockInsertion(insert, write_pin);
|
|
|
|
|
}
|
|
|
|
|
else if (clk) {
|
|
|
|
|
WriteGetClock write_clk(clk, this);
|
|
|
|
|
writeClockInsertion(insert, write_clk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockInsertion(ClockInsertion *insert,
|
|
|
|
|
WriteSdcObject &write_obj) const
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
RiseFallMinMax *early_values = insert->delays(EarlyLate::early());
|
|
|
|
|
RiseFallMinMax *late_values = insert->delays(EarlyLate::late());
|
2018-09-28 17:54:21 +02:00
|
|
|
if (early_values->equal(late_values))
|
|
|
|
|
writeRiseFallMinMaxTimeCmd("set_clock_latency -source",
|
|
|
|
|
late_values, write_obj);
|
|
|
|
|
else {
|
|
|
|
|
writeRiseFallMinMaxTimeCmd("set_clock_latency -source -early",
|
|
|
|
|
early_values, write_obj);
|
|
|
|
|
writeRiseFallMinMaxTimeCmd("set_clock_latency -source -late",
|
|
|
|
|
late_values, write_obj);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writePropagatedClkPins() const
|
|
|
|
|
{
|
|
|
|
|
PinSet::Iterator pin_iter(sdc_->propagated_clk_pins_);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
const Pin *pin = pin_iter.next();
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_propagated_clock ");
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(pin, true);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeInterClockUncertainties() const
|
|
|
|
|
{
|
|
|
|
|
InterClockUncertaintySet::Iterator
|
|
|
|
|
uncertainty_iter(sdc_->inter_clk_uncertainties_);
|
|
|
|
|
while (uncertainty_iter.hasNext()) {
|
|
|
|
|
InterClockUncertainty *uncertainty = uncertainty_iter.next();
|
|
|
|
|
writeInterClockUncertainty(uncertainty);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::
|
|
|
|
|
writeInterClockUncertainty(InterClockUncertainty *uncertainty) const
|
|
|
|
|
{
|
|
|
|
|
const Clock *src_clk = uncertainty->src();
|
|
|
|
|
const Clock *tgt_clk = uncertainty->target();
|
|
|
|
|
const RiseFallMinMax *src_rise =
|
2019-11-11 23:30:19 +01:00
|
|
|
uncertainty->uncertainties(RiseFall::rise());
|
2018-09-28 17:54:21 +02:00
|
|
|
const RiseFallMinMax *src_fall =
|
2019-11-11 23:30:19 +01:00
|
|
|
uncertainty->uncertainties(RiseFall::fall());
|
2018-09-28 17:54:21 +02:00
|
|
|
float value;
|
|
|
|
|
if (src_rise->equal(src_fall)
|
|
|
|
|
&& src_rise->isOneValue(value)) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_clock_uncertainty -from ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetClock(src_clk);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -to ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetClock(tgt_clk);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeTime(value);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2019-11-11 23:30:19 +01:00
|
|
|
for (auto src_rf : RiseFall::range()) {
|
|
|
|
|
for (auto tgt_rf : RiseFall::range()) {
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto setup_hold : SetupHold::range()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
float value;
|
|
|
|
|
bool exists;
|
2019-11-11 23:30:19 +01:00
|
|
|
sdc_->clockUncertainty(src_clk, src_rf, tgt_clk, tgt_rf,
|
2018-09-28 17:54:21 +02:00
|
|
|
setup_hold, value, exists);
|
|
|
|
|
if (exists) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_clock_uncertainty -%s_from ",
|
2019-11-11 23:30:19 +01:00
|
|
|
src_rf == RiseFall::rise() ? "rise" : "fall");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetClock(uncertainty->src());
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -%s_to ",
|
2019-11-11 23:30:19 +01:00
|
|
|
tgt_rf == RiseFall::rise() ? "rise" : "fall");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetClock(uncertainty->target());
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " %s ",
|
2018-09-28 17:54:21 +02:00
|
|
|
setupHoldFlag(setup_hold));
|
|
|
|
|
writeTime(value);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeInputDelays() const
|
|
|
|
|
{
|
|
|
|
|
// Sort arrivals by pin and clock name.
|
|
|
|
|
PortDelaySeq delays;
|
2019-10-25 17:51:59 +02:00
|
|
|
for (InputDelay *input_delay : sdc_->inputDelays())
|
2018-09-28 17:54:21 +02:00
|
|
|
delays.push_back(input_delay);
|
2019-03-13 01:25:53 +01:00
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
PortDelayLess port_delay_less(sdc_network_);
|
|
|
|
|
sort(delays, port_delay_less);
|
2019-03-13 01:25:53 +01:00
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
PortDelaySeq::Iterator delay_iter(delays);
|
|
|
|
|
while (delay_iter.hasNext()) {
|
|
|
|
|
PortDelay *input_delay = delay_iter.next();
|
2019-10-25 17:51:59 +02:00
|
|
|
writePortDelay(input_delay, true, "set_input_delay");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeOutputDelays() const
|
|
|
|
|
{
|
|
|
|
|
// Sort departures by pin and clock name.
|
|
|
|
|
PortDelaySeq delays;
|
2019-10-25 17:51:59 +02:00
|
|
|
for (OutputDelay *output_delay : sdc_->outputDelays())
|
2018-09-28 17:54:21 +02:00
|
|
|
delays.push_back(output_delay);
|
2019-03-13 01:25:53 +01:00
|
|
|
|
2023-01-19 19:23:45 +01:00
|
|
|
sort(delays, PortDelayLess(sdc_network_));
|
2019-03-13 01:25:53 +01:00
|
|
|
|
2023-01-19 19:23:45 +01:00
|
|
|
for (PortDelay *output_delay : delays)
|
2019-10-25 17:51:59 +02:00
|
|
|
writePortDelay(output_delay, false, "set_output_delay");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writePortDelay(PortDelay *port_delay,
|
2019-10-25 17:51:59 +02:00
|
|
|
bool is_input_delay,
|
2018-09-28 17:54:21 +02:00
|
|
|
const char *sdc_cmd) const
|
|
|
|
|
{
|
|
|
|
|
RiseFallMinMax *delays = port_delay->delays();
|
|
|
|
|
float rise_min, rise_max, fall_min, fall_max;
|
|
|
|
|
bool rise_min_exists, rise_max_exists, fall_min_exists, fall_max_exists;
|
2019-11-11 23:30:19 +01:00
|
|
|
delays->value(RiseFall::rise(), MinMax::min(),
|
2018-09-28 17:54:21 +02:00
|
|
|
rise_min, rise_min_exists);
|
2019-11-11 23:30:19 +01:00
|
|
|
delays->value(RiseFall::rise(), MinMax::max(),
|
2018-09-28 17:54:21 +02:00
|
|
|
rise_max, rise_max_exists);
|
2019-11-11 23:30:19 +01:00
|
|
|
delays->value(RiseFall::fall(), MinMax::min(),
|
2018-09-28 17:54:21 +02:00
|
|
|
fall_min, fall_min_exists);
|
2019-11-11 23:30:19 +01:00
|
|
|
delays->value(RiseFall::fall(), MinMax::max(),
|
2018-09-28 17:54:21 +02:00
|
|
|
fall_max, fall_max_exists);
|
|
|
|
|
// Try to compress the four port delays.
|
|
|
|
|
if (rise_min_exists
|
|
|
|
|
&& rise_max_exists
|
|
|
|
|
&& fall_min_exists
|
|
|
|
|
&& fall_max_exists
|
|
|
|
|
&& rise_max == rise_min
|
|
|
|
|
&& fall_min == rise_min
|
|
|
|
|
&& fall_max == rise_min)
|
2019-10-25 17:51:59 +02:00
|
|
|
writePortDelay(port_delay, is_input_delay, rise_min,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::riseFall(), MinMaxAll::all(), sdc_cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
else if (rise_min_exists
|
|
|
|
|
&& rise_max_exists
|
|
|
|
|
&& rise_max == rise_min
|
|
|
|
|
&& fall_min_exists
|
|
|
|
|
&& fall_max_exists
|
|
|
|
|
&& fall_min == fall_max) {
|
2019-10-25 17:51:59 +02:00
|
|
|
writePortDelay(port_delay, is_input_delay, rise_min,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::rise(), MinMaxAll::all(), sdc_cmd);
|
2019-10-25 17:51:59 +02:00
|
|
|
writePortDelay(port_delay, is_input_delay, fall_min,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::fall(), MinMaxAll::all(), sdc_cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else if (rise_min_exists
|
|
|
|
|
&& fall_min_exists
|
|
|
|
|
&& rise_min == fall_min
|
|
|
|
|
&& rise_max_exists
|
|
|
|
|
&& fall_max_exists
|
|
|
|
|
&& rise_max == fall_max) {
|
2019-10-25 17:51:59 +02:00
|
|
|
writePortDelay(port_delay, is_input_delay, rise_min,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::riseFall(), MinMaxAll::min(), sdc_cmd);
|
2019-10-25 17:51:59 +02:00
|
|
|
writePortDelay(port_delay, is_input_delay, rise_max,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::riseFall(), MinMaxAll::max(), sdc_cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (rise_min_exists)
|
2019-10-25 17:51:59 +02:00
|
|
|
writePortDelay(port_delay, is_input_delay, rise_min,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::rise(), MinMaxAll::min(), sdc_cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (rise_max_exists)
|
2019-10-25 17:51:59 +02:00
|
|
|
writePortDelay(port_delay, is_input_delay, rise_max,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::rise(), MinMaxAll::max(), sdc_cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (fall_min_exists)
|
2019-10-25 17:51:59 +02:00
|
|
|
writePortDelay(port_delay, is_input_delay, fall_min,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::fall(), MinMaxAll::min(), sdc_cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (fall_max_exists)
|
2019-10-25 17:51:59 +02:00
|
|
|
writePortDelay(port_delay, is_input_delay, fall_max,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::fall(), MinMaxAll::max(), sdc_cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writePortDelay(PortDelay *port_delay,
|
2019-10-25 17:51:59 +02:00
|
|
|
bool is_input_delay,
|
2018-09-28 17:54:21 +02:00
|
|
|
float delay,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFallBoth *rf,
|
2018-09-28 17:54:21 +02:00
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
const char *sdc_cmd) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s ", sdc_cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeTime(delay);
|
2023-01-19 19:23:45 +01:00
|
|
|
const ClockEdge *clk_edge = port_delay->clkEdge();
|
2018-09-28 17:54:21 +02:00
|
|
|
if (clk_edge) {
|
|
|
|
|
writeClockKey(clk_edge->clock());
|
2019-11-11 23:30:19 +01:00
|
|
|
if (clk_edge->transition() == RiseFall::fall())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -clock_fall");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s%s -add_delay ",
|
2019-11-11 23:30:19 +01:00
|
|
|
transRiseFallFlag(rf),
|
2018-09-28 17:54:21 +02:00
|
|
|
minMaxFlag(min_max));
|
2023-01-19 19:23:45 +01:00
|
|
|
const Pin *ref_pin = port_delay->refPin();
|
2018-09-28 17:54:21 +02:00
|
|
|
if (ref_pin) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "-reference_pin ");
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(ref_pin, true);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(port_delay->pin(), is_input_delay);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class PinClockPairNameLess
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
PinClockPairNameLess(const Network *network);
|
2019-03-13 01:25:53 +01:00
|
|
|
bool operator()(const PinClockPair &pin_clk1,
|
|
|
|
|
const PinClockPair &pin_clk2) const;
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
PinPathNameLess pin_less_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PinClockPairNameLess::PinClockPairNameLess(const Network *network) :
|
|
|
|
|
pin_less_(network)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2019-03-13 01:25:53 +01:00
|
|
|
PinClockPairNameLess::operator()(const PinClockPair &pin_clk1,
|
|
|
|
|
const PinClockPair &pin_clk2) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
const Pin *pin1 = pin_clk1.first;
|
|
|
|
|
const Pin *pin2 = pin_clk2.first;
|
|
|
|
|
const Clock *clk1 = pin_clk1.second;
|
|
|
|
|
const Clock *clk2 = pin_clk2.second;
|
2018-09-28 17:54:21 +02:00
|
|
|
return pin_less_(pin1, pin2)
|
|
|
|
|
|| (pin1 == pin2
|
2019-03-13 01:25:53 +01:00
|
|
|
&& ((clk1 == nullptr && clk2)
|
2018-09-28 17:54:21 +02:00
|
|
|
|| (clk1 && clk2
|
|
|
|
|
&& clk1->index() < clk2->index())));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockSenses() const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
Vector<PinClockPair> pin_clks;
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto& [pin_clk, sense] : sdc_->clk_sense_map_)
|
|
|
|
|
pin_clks.push_back(pin_clk);
|
2019-03-13 01:25:53 +01:00
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
// Sort by pin/clk pair so regressions results are stable.
|
|
|
|
|
sort(pin_clks, PinClockPairNameLess(sdc_network_));
|
2019-03-13 01:25:53 +01:00
|
|
|
|
|
|
|
|
for (auto pin_clk : pin_clks) {
|
2018-09-28 17:54:21 +02:00
|
|
|
ClockSense sense;
|
|
|
|
|
bool exists;
|
|
|
|
|
sdc_->clk_sense_map_.findKey(pin_clk, sense, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
writeClockSense(pin_clk, sense);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2019-03-13 01:25:53 +01:00
|
|
|
WriteSdc::writeClockSense(PinClockPair &pin_clk,
|
2018-09-28 17:54:21 +02:00
|
|
|
ClockSense sense) const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
const char *flag = nullptr;
|
|
|
|
|
if (sense == ClockSense::positive)
|
2018-09-28 17:54:21 +02:00
|
|
|
flag = "-positive";
|
2019-03-13 01:25:53 +01:00
|
|
|
else if (sense == ClockSense::negative)
|
2018-09-28 17:54:21 +02:00
|
|
|
flag = "-negative";
|
2019-03-13 01:25:53 +01:00
|
|
|
else if (sense == ClockSense::stop)
|
2018-09-28 17:54:21 +02:00
|
|
|
flag = "-stop_propagation";
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_sense -type clock %s ", flag);
|
2019-03-13 01:25:53 +01:00
|
|
|
const Clock *clk = pin_clk.second;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (clk) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "-clock ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetClock(clk);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(pin_clk.first, true);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ClockGroupLess
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
bool operator()(const ClockGroup *clk_group1,
|
|
|
|
|
const ClockGroup *clk_group2) const;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ClockGroupLess::operator()(const ClockGroup *clk_group1,
|
|
|
|
|
const ClockGroup *clk_group2) const
|
|
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
size_t size1 = clk_group1->size();
|
|
|
|
|
size_t size2 = clk_group2->size();
|
2018-09-28 17:54:21 +02:00
|
|
|
if (size1 < size2)
|
|
|
|
|
return true;
|
|
|
|
|
else if (size1 > size2)
|
|
|
|
|
return false;
|
|
|
|
|
else {
|
2023-01-19 19:23:45 +01:00
|
|
|
ClockSeq clks1;
|
|
|
|
|
for (Clock *clk1 : *clk_group1)
|
|
|
|
|
clks1.push_back(clk1);
|
2018-09-28 17:54:21 +02:00
|
|
|
sort(clks1, ClockNameLess());
|
|
|
|
|
|
2023-01-19 19:23:45 +01:00
|
|
|
ClockSeq clks2;
|
|
|
|
|
for (Clock *clk2 : *clk_group2)
|
|
|
|
|
clks2.push_back(clk2);
|
2018-09-28 17:54:21 +02:00
|
|
|
sort(clks2, ClockNameLess());
|
|
|
|
|
|
|
|
|
|
ClockSeq::Iterator clk_iter3(clks1);
|
|
|
|
|
ClockSeq::Iterator clk_iter4(clks2);
|
|
|
|
|
while (clk_iter3.hasNext()
|
|
|
|
|
&& clk_iter4.hasNext()) {
|
|
|
|
|
Clock *clk1 = clk_iter3.next();
|
|
|
|
|
Clock *clk2 = clk_iter4.next();
|
|
|
|
|
int cmp = strcmp(clk1->name(), clk2->name());
|
|
|
|
|
if (cmp < 0)
|
|
|
|
|
return true;
|
|
|
|
|
else if (cmp > 0)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockGroups() const
|
|
|
|
|
{
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [name, clk_groups] : sdc_->clk_groups_name_map_)
|
2018-09-28 17:54:21 +02:00
|
|
|
writeClockGroups(clk_groups);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockGroups(ClockGroups *clk_groups) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_clock_groups -name %s ", clk_groups->name());
|
2018-09-28 17:54:21 +02:00
|
|
|
if (clk_groups->logicallyExclusive())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "-logically_exclusive \\\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
else if (clk_groups->physicallyExclusive())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "-physically_exclusive \\\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
else if (clk_groups->asynchronous())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "-asynchronous \\\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
if (clk_groups->allowPaths())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "-allow_paths \\\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
Vector<ClockGroup*> groups;
|
|
|
|
|
ClockGroupSet::Iterator group_iter1(clk_groups->groups());
|
|
|
|
|
while (group_iter1.hasNext()) {
|
|
|
|
|
ClockGroup *clk_group = group_iter1.next();
|
|
|
|
|
groups.push_back(clk_group);
|
|
|
|
|
}
|
|
|
|
|
sort(groups, ClockGroupLess());
|
|
|
|
|
bool first = true;
|
|
|
|
|
Vector<ClockGroup*>::Iterator group_iter2(groups);
|
|
|
|
|
while (group_iter2.hasNext()) {
|
|
|
|
|
ClockGroup *clk_group = group_iter2.next();
|
|
|
|
|
if (!first)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\\\n");
|
|
|
|
|
gzprintf(stream_, " -group ");
|
2023-01-19 19:23:45 +01:00
|
|
|
writeGetClocks(clk_group);
|
2018-09-28 17:54:21 +02:00
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
writeCmdComment(clk_groups);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisables() const
|
|
|
|
|
{
|
|
|
|
|
writeDisabledCells();
|
|
|
|
|
writeDisabledPorts();
|
|
|
|
|
writeDisabledLibPorts();
|
|
|
|
|
writeDisabledInstances();
|
|
|
|
|
writeDisabledPins();
|
|
|
|
|
writeDisabledEdges();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledCells() const
|
|
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
DisabledCellPortsSeq disables = sortByName(sdc_->disabledCellPorts());
|
|
|
|
|
for (const DisabledCellPorts *disable : disables) {
|
|
|
|
|
const LibertyCell *cell = disable->cell();
|
2018-09-28 17:54:21 +02:00
|
|
|
if (disable->all()) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetLibCell(cell);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
if (disable->fromTo()) {
|
2023-01-19 19:23:45 +01:00
|
|
|
LibertyPortPairSeq from_tos = sortByName(disable->fromTo());
|
|
|
|
|
for (const LibertyPortPair &from_to : from_tos) {
|
|
|
|
|
const LibertyPort *from = from_to.first;
|
|
|
|
|
const LibertyPort *to = from_to.second;
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing -from {%s} -to {%s} ",
|
2018-09-28 17:54:21 +02:00
|
|
|
from->name(),
|
|
|
|
|
to->name());
|
|
|
|
|
writeGetLibCell(cell);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (disable->from()) {
|
2023-01-19 19:23:45 +01:00
|
|
|
LibertyPortSeq from = sortByName(disable->from());
|
|
|
|
|
for (const LibertyPort *from_port : from) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing -from {%s} ",
|
2018-09-28 17:54:21 +02:00
|
|
|
from_port->name());
|
|
|
|
|
writeGetLibCell(cell);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (disable->to()) {
|
2023-01-19 19:23:45 +01:00
|
|
|
LibertyPortSeq to = sortByName(disable->to());
|
|
|
|
|
for (const LibertyPort *to_port : to) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing -to {%s} ",
|
2018-09-28 17:54:21 +02:00
|
|
|
to_port->name());
|
|
|
|
|
writeGetLibCell(cell);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (disable->timingArcSets()) {
|
|
|
|
|
// The only syntax to disable timing arc sets disables all of the
|
|
|
|
|
// cell's timing arc sets.
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetTimingArcsOfOjbects(cell);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledPorts() const
|
|
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
const PortSeq ports = sortByName(sdc_->disabledPorts(), sdc_network_);
|
|
|
|
|
for (const Port *port : ports) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetPort(port);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledLibPorts() const
|
|
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
LibertyPortSeq ports = sortByName(sdc_->disabledLibPorts());
|
|
|
|
|
for (LibertyPort *port : ports) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetLibPin(port);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledInstances() const
|
|
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
DisabledInstancePortsSeq disables = sortByPathName(sdc_->disabledInstancePorts(),
|
|
|
|
|
sdc_network_);
|
|
|
|
|
for (DisabledInstancePorts *disable : disables) {
|
2018-09-28 17:54:21 +02:00
|
|
|
Instance *inst = disable->instance();
|
|
|
|
|
if (disable->all()) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetInstance(inst);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else if (disable->fromTo()) {
|
2023-01-19 19:23:45 +01:00
|
|
|
LibertyPortPairSeq from_tos = sortByName(disable->fromTo());
|
|
|
|
|
for (LibertyPortPair &from_to : from_tos) {
|
|
|
|
|
const LibertyPort *from_port = from_to.first;
|
|
|
|
|
const LibertyPort *to_port = from_to.second;
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing -from {%s} -to {%s} ",
|
2018-09-28 17:54:21 +02:00
|
|
|
from_port->name(),
|
|
|
|
|
to_port->name());
|
|
|
|
|
writeGetInstance(inst);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (disable->from()) {
|
2023-01-19 19:23:45 +01:00
|
|
|
LibertyPortSeq from = sortByName(disable->from());
|
|
|
|
|
for (const LibertyPort *from_port : from) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing -from {%s} ",
|
2018-09-28 17:54:21 +02:00
|
|
|
from_port->name());
|
|
|
|
|
writeGetInstance(inst);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (disable->to()) {
|
2023-01-19 19:23:45 +01:00
|
|
|
LibertyPortSeq to = sortByName(disable->to());
|
|
|
|
|
for (const LibertyPort *to_port : to) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing -to {%s} ",
|
2018-09-28 17:54:21 +02:00
|
|
|
to_port->name());
|
|
|
|
|
writeGetInstance(inst);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledPins() const
|
|
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
PinSeq pins = sortByPathName(sdc_->disabledPins(), sdc_network_);
|
|
|
|
|
for (const Pin *pin : pins) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing ");
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(pin, false);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledEdges() const
|
|
|
|
|
{
|
|
|
|
|
EdgeSeq edges;
|
2023-01-19 19:23:45 +01:00
|
|
|
for (Edge *edge : *sdc_->disabledEdges())
|
2018-09-28 17:54:21 +02:00
|
|
|
edges.push_back(edge);
|
|
|
|
|
sortEdges(&edges, sdc_network_, graph_);
|
2023-01-19 19:23:45 +01:00
|
|
|
for (Edge *edge : edges) {
|
2018-09-28 17:54:21 +02:00
|
|
|
EdgeSet matches;
|
|
|
|
|
findMatchingEdges(edge, matches);
|
|
|
|
|
if (matches.size() == 1)
|
|
|
|
|
writeDisabledEdge(edge);
|
|
|
|
|
else if (edgeSenseIsUnique(edge, matches))
|
|
|
|
|
writeDisabledEdgeSense(edge);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::findMatchingEdges(Edge *edge,
|
|
|
|
|
EdgeSet &matches) const
|
|
|
|
|
{
|
|
|
|
|
Vertex *from_vertex = edge->from(graph_);
|
|
|
|
|
Vertex *to_vertex = edge->to(graph_);
|
|
|
|
|
Pin *to_pin = to_vertex->pin();
|
|
|
|
|
VertexOutEdgeIterator edge_iter(from_vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *out_edge = edge_iter.next();
|
|
|
|
|
if (out_edge->to(graph_)->pin() == to_pin)
|
|
|
|
|
matches.insert(out_edge);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
WriteSdc::edgeSenseIsUnique(Edge *edge,
|
|
|
|
|
EdgeSet &matches) const
|
|
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
for (Edge *match : matches) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (match != edge
|
|
|
|
|
&& match->sense() == edge->sense())
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledEdge(Edge *edge) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetTimingArcs(edge);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledEdgeSense(Edge *edge) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_disable_timing ");
|
2025-03-31 00:27:53 +02:00
|
|
|
const char *sense = to_string(edge->sense());
|
2019-01-17 00:37:31 +01:00
|
|
|
string filter;
|
|
|
|
|
stringPrint(filter, "sense == %s", sense);
|
|
|
|
|
writeGetTimingArcs(edge, filter.c_str());
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptions() const
|
|
|
|
|
{
|
|
|
|
|
ExceptionPathSeq exceptions;
|
2023-01-19 19:23:45 +01:00
|
|
|
for (ExceptionPath *exception : *sdc_->exceptions())
|
|
|
|
|
exceptions.push_back(exception);
|
|
|
|
|
sort(exceptions, ExceptionPathLess(network_));
|
|
|
|
|
for (ExceptionPath *exception : exceptions) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!exception->isFilter()
|
|
|
|
|
&& !exception->isLoop())
|
|
|
|
|
writeException(exception);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeException(ExceptionPath *exception) const
|
|
|
|
|
{
|
|
|
|
|
writeExceptionCmd(exception);
|
|
|
|
|
if (exception->from())
|
|
|
|
|
writeExceptionFrom(exception->from());
|
|
|
|
|
if (exception->thrus()) {
|
2023-01-19 19:23:45 +01:00
|
|
|
for (ExceptionThru *thru : *exception->thrus())
|
2018-09-28 17:54:21 +02:00
|
|
|
writeExceptionThru(thru);
|
|
|
|
|
}
|
|
|
|
|
if (exception->to())
|
|
|
|
|
writeExceptionTo(exception->to());
|
|
|
|
|
writeExceptionValue(exception);
|
|
|
|
|
writeCmdComment(exception);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptionCmd(ExceptionPath *exception) const
|
|
|
|
|
{
|
|
|
|
|
if (exception->isFalse()) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_false_path");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeSetupHoldFlag(exception->minMax());
|
|
|
|
|
}
|
|
|
|
|
else if (exception->isMultiCycle()) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_multicycle_path");
|
2018-09-28 17:54:21 +02:00
|
|
|
const MinMaxAll *min_max = exception->minMax();
|
|
|
|
|
writeSetupHoldFlag(min_max);
|
|
|
|
|
if (min_max == MinMaxAll::min()) {
|
|
|
|
|
// For hold MCPs default is -start.
|
|
|
|
|
if (exception->useEndClk())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -end");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// For setup MCPs default is -end.
|
|
|
|
|
if (!exception->useEndClk())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -start");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (exception->isPathDelay()) {
|
|
|
|
|
if (exception->minMax() == MinMaxAll::max())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_max_delay");
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_min_delay");
|
2018-09-28 17:54:21 +02:00
|
|
|
if (exception->ignoreClkLatency())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -ignore_clock_latency");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else if (exception->isGroupPath()) {
|
|
|
|
|
if (exception->isDefault())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "group_path -default");
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "group_path -name %s", exception->name());
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else
|
2024-01-08 03:23:53 +01:00
|
|
|
report_->critical(1620, "unknown exception type");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptionValue(ExceptionPath *exception) const
|
|
|
|
|
{
|
|
|
|
|
if (exception->isMultiCycle())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " %d",
|
2018-09-28 17:54:21 +02:00
|
|
|
exception->pathMultiplier());
|
|
|
|
|
else if (exception->isPathDelay()) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeTime(exception->delay());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptionFrom(ExceptionFrom *from) const
|
|
|
|
|
{
|
2019-10-25 17:51:59 +02:00
|
|
|
writeExceptionFromTo(from, "from", true);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptionTo(ExceptionTo *to) const
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFallBoth *end_rf = to->endTransition();
|
|
|
|
|
if (end_rf != RiseFallBoth::riseFall())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s ", transRiseFallFlag(end_rf));
|
2018-09-28 17:54:21 +02:00
|
|
|
if (to->hasObjects())
|
2019-10-25 17:51:59 +02:00
|
|
|
writeExceptionFromTo(to, "to", false);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptionFromTo(ExceptionFromTo *from_to,
|
2019-10-25 17:51:59 +02:00
|
|
|
const char *from_to_key,
|
|
|
|
|
bool map_hpin_to_drvr) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFallBoth *rf = from_to->transition();
|
2021-11-22 16:37:59 +01:00
|
|
|
const char *rf_prefix = "-";
|
2019-11-11 23:30:19 +01:00
|
|
|
if (rf == RiseFallBoth::rise())
|
2021-11-22 16:37:59 +01:00
|
|
|
rf_prefix = "-rise_";
|
2019-11-11 23:30:19 +01:00
|
|
|
else if (rf == RiseFallBoth::fall())
|
2021-11-22 16:37:59 +01:00
|
|
|
rf_prefix = "-fall_";
|
|
|
|
|
gzprintf(stream_, "\\\n %s%s ", rf_prefix, from_to_key);
|
2018-09-28 17:54:21 +02:00
|
|
|
bool multi_objs =
|
|
|
|
|
((from_to->pins() ? from_to->pins()->size() : 0)
|
|
|
|
|
+ (from_to->clks() ? from_to->clks()->size() : 0)
|
|
|
|
|
+ (from_to->instances() ? from_to->instances()->size() : 0)) > 1;
|
|
|
|
|
if (multi_objs)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[list ");
|
2018-09-28 17:54:21 +02:00
|
|
|
bool first = true;
|
|
|
|
|
if (from_to->pins()) {
|
2023-01-19 19:23:45 +01:00
|
|
|
PinSeq pins = sortByPathName(from_to->pins(), sdc_network_);
|
|
|
|
|
for (const Pin *pin : pins) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (multi_objs && !first)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\\\n ");
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(pin, map_hpin_to_drvr);
|
2018-09-28 17:54:21 +02:00
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (from_to->clks())
|
|
|
|
|
writeGetClocks(from_to->clks(), multi_objs, first);
|
|
|
|
|
if (from_to->instances()) {
|
2023-01-19 19:23:45 +01:00
|
|
|
InstanceSeq insts = sortByPathName(from_to->instances(), sdc_network_);
|
|
|
|
|
for (const Instance *inst : insts) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (multi_objs && !first)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\\\n ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetInstance(inst);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (multi_objs)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "]");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptionThru(ExceptionThru *thru) const
|
|
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFallBoth *rf = thru->transition();
|
2021-11-22 16:37:59 +01:00
|
|
|
const char *rf_prefix = "-";
|
2019-11-11 23:30:19 +01:00
|
|
|
if (rf == RiseFallBoth::rise())
|
2021-11-22 16:37:59 +01:00
|
|
|
rf_prefix = "-rise_";
|
2019-11-11 23:30:19 +01:00
|
|
|
else if (rf == RiseFallBoth::fall())
|
2021-11-22 16:37:59 +01:00
|
|
|
rf_prefix = "-fall_";
|
|
|
|
|
gzprintf(stream_, "\\\n %sthrough ", rf_prefix);
|
2019-10-25 17:51:59 +02:00
|
|
|
PinSeq pins;
|
|
|
|
|
mapThruHpins(thru, pins);
|
2018-09-28 17:54:21 +02:00
|
|
|
bool multi_objs =
|
2019-10-25 17:51:59 +02:00
|
|
|
(pins.size()
|
2018-09-28 17:54:21 +02:00
|
|
|
+ (thru->nets() ? thru->nets()->size() : 0)
|
2019-10-25 17:51:59 +02:00
|
|
|
+ (thru->instances() ? thru->instances()->size() : 0)) > 1;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (multi_objs)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[list ");
|
2018-09-28 17:54:21 +02:00
|
|
|
bool first = true;
|
2019-10-25 17:51:59 +02:00
|
|
|
sort(pins, PinPathNameLess(network_));
|
2023-01-19 19:23:45 +01:00
|
|
|
for (const Pin *pin : pins) {
|
2019-10-25 17:51:59 +02:00
|
|
|
if (multi_objs && !first)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\\\n ");
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(pin);
|
|
|
|
|
first = false;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2019-10-25 17:51:59 +02:00
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
if (thru->nets()) {
|
2023-01-19 19:23:45 +01:00
|
|
|
NetSeq nets = sortByPathName(thru->nets(), sdc_network_);
|
|
|
|
|
for (const Net *net : nets) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (multi_objs && !first)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\\\n ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetNet(net);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (thru->instances()) {
|
2023-01-19 19:23:45 +01:00
|
|
|
InstanceSeq insts = sortByPathName(thru->instances(), sdc_network_);
|
|
|
|
|
for (const Instance *inst : insts) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (multi_objs && !first)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\\\n ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetInstance(inst);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (multi_objs)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "]");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-25 17:51:59 +02:00
|
|
|
void
|
|
|
|
|
WriteSdc::mapThruHpins(ExceptionThru *thru,
|
|
|
|
|
PinSeq &pins) const
|
|
|
|
|
{
|
|
|
|
|
if (thru->pins()) {
|
2023-01-19 19:23:45 +01:00
|
|
|
for (const Pin *pin : *thru->pins()) {
|
2019-10-25 17:51:59 +02:00
|
|
|
// Map hierarical pins to load pins outside of outputs or inside of inputs.
|
|
|
|
|
if (network_->isHierarchical(pin)) {
|
|
|
|
|
Instance *hinst = network_->instance(pin);
|
|
|
|
|
bool hpin_is_output = network_->direction(pin)->isAnyOutput();
|
|
|
|
|
PinConnectedPinIterator *cpin_iter = network_->connectedPinIterator(pin);
|
|
|
|
|
while (cpin_iter->hasNext()) {
|
2023-01-19 19:23:45 +01:00
|
|
|
const Pin *cpin = cpin_iter->next();
|
2019-10-25 17:51:59 +02:00
|
|
|
if (network_->isLoad(cpin)
|
|
|
|
|
&& ((hpin_is_output
|
|
|
|
|
&& !network_->isInside(network_->instance(cpin), hinst))
|
|
|
|
|
|| (!hpin_is_output
|
|
|
|
|
&& network_->isInside(network_->instance(cpin), hinst))))
|
|
|
|
|
pins.push_back(cpin);
|
|
|
|
|
}
|
2019-11-11 04:27:59 +01:00
|
|
|
delete cpin_iter;
|
2019-10-25 17:51:59 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
pins.push_back(pin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDataChecks() const
|
|
|
|
|
{
|
|
|
|
|
Vector<DataCheck*> checks;
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [pin, checks1] : sdc_->data_checks_to_map_) {
|
2023-01-19 19:23:45 +01:00
|
|
|
for (DataCheck *check : *checks1)
|
2018-09-28 17:54:21 +02:00
|
|
|
checks.push_back(check);
|
|
|
|
|
}
|
|
|
|
|
sort(checks, DataCheckLess(sdc_network_));
|
2023-01-19 19:23:45 +01:00
|
|
|
for (DataCheck *check : checks)
|
2018-09-28 17:54:21 +02:00
|
|
|
writeDataCheck(check);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDataCheck(DataCheck *check) const
|
|
|
|
|
{
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto setup_hold : SetupHold::range()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
float margin;
|
|
|
|
|
bool one_value;
|
|
|
|
|
check->marginIsOneValue(setup_hold, margin, one_value);
|
|
|
|
|
if (one_value)
|
2019-11-11 23:30:19 +01:00
|
|
|
writeDataCheck(check, RiseFallBoth::riseFall(),
|
|
|
|
|
RiseFallBoth::riseFall(), setup_hold, margin);
|
2018-09-28 17:54:21 +02:00
|
|
|
else {
|
2019-11-11 23:30:19 +01:00
|
|
|
for (auto from_rf : RiseFall::range()) {
|
|
|
|
|
for (auto to_rf : RiseFall::range()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
float margin;
|
|
|
|
|
bool margin_exists;
|
2019-11-11 23:30:19 +01:00
|
|
|
check->margin(from_rf, to_rf, setup_hold, margin, margin_exists);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (margin_exists) {
|
2019-11-11 23:30:19 +01:00
|
|
|
writeDataCheck(check, from_rf->asRiseFallBoth(),
|
|
|
|
|
to_rf->asRiseFallBoth(), setup_hold, margin);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDataCheck(DataCheck *check,
|
2025-03-31 00:27:53 +02:00
|
|
|
const RiseFallBoth *from_rf,
|
|
|
|
|
const RiseFallBoth *to_rf,
|
|
|
|
|
const SetupHold *setup_hold,
|
2018-09-28 17:54:21 +02:00
|
|
|
float margin) const
|
|
|
|
|
{
|
|
|
|
|
const char *from_key = "-from";
|
2019-11-11 23:30:19 +01:00
|
|
|
if (from_rf == RiseFallBoth::rise())
|
2018-09-28 17:54:21 +02:00
|
|
|
from_key = "-rise_from";
|
2019-11-11 23:30:19 +01:00
|
|
|
else if (from_rf == RiseFallBoth::fall())
|
2018-09-28 17:54:21 +02:00
|
|
|
from_key = "-fall_from";
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_data_check %s ", from_key);
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(check->from(), true);
|
2018-09-28 17:54:21 +02:00
|
|
|
const char *to_key = "-to";
|
2019-11-11 23:30:19 +01:00
|
|
|
if (to_rf == RiseFallBoth::rise())
|
2018-09-28 17:54:21 +02:00
|
|
|
to_key = "-rise_to";
|
2019-11-11 23:30:19 +01:00
|
|
|
else if (to_rf == RiseFallBoth::fall())
|
2018-09-28 17:54:21 +02:00
|
|
|
to_key = "-fall_to";
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " %s ", to_key);
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(check->to(), false);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s ",
|
2018-09-28 17:54:21 +02:00
|
|
|
setupHoldFlag(setup_hold));
|
|
|
|
|
writeTime(margin);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeEnvironment() const
|
|
|
|
|
{
|
|
|
|
|
writeCommentSection("Environment");
|
|
|
|
|
writeOperatingConditions();
|
|
|
|
|
writeWireload();
|
2020-07-06 01:37:50 +02:00
|
|
|
writePortLoads();
|
|
|
|
|
writeNetLoads();
|
2018-09-28 17:54:21 +02:00
|
|
|
writeDriveResistances();
|
|
|
|
|
writeDrivingCells();
|
|
|
|
|
writeInputTransitions();
|
|
|
|
|
writeNetResistances();
|
|
|
|
|
writeConstants();
|
|
|
|
|
writeCaseAnalysis();
|
|
|
|
|
writeDeratings();
|
2023-09-09 02:51:21 +02:00
|
|
|
writeVoltages();
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeOperatingConditions() const
|
|
|
|
|
{
|
|
|
|
|
OperatingConditions *cond = sdc_->operatingConditions(MinMax::max());
|
|
|
|
|
if (cond)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_operating_conditions %s\n", cond->name());
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeWireload() const
|
|
|
|
|
{
|
|
|
|
|
WireloadMode wireload_mode = sdc_->wireloadMode();
|
2019-03-13 01:25:53 +01:00
|
|
|
if (wireload_mode != WireloadMode::unknown)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_wire_load_mode \"%s\"\n",
|
2018-09-28 17:54:21 +02:00
|
|
|
wireloadModeString(wireload_mode));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-07-06 01:37:50 +02:00
|
|
|
WriteSdc::writeNetLoads() const
|
|
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
int corner_index = 0; // missing corner arg
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [net, caps] : sdc_->net_wire_cap_maps_[corner_index]) {
|
2023-01-19 19:23:45 +01:00
|
|
|
float min_cap, max_cap;
|
|
|
|
|
bool min_exists, max_exists;
|
|
|
|
|
caps.value(MinMax::min(), min_cap, min_exists);
|
|
|
|
|
caps.value(MinMax::max(), max_cap, max_exists);
|
|
|
|
|
if (min_exists && max_exists
|
|
|
|
|
&& min_cap == max_cap)
|
|
|
|
|
writeNetLoad(net, MinMaxAll::all(), min_cap);
|
|
|
|
|
else {
|
|
|
|
|
if (min_exists)
|
|
|
|
|
writeNetLoad(net, MinMaxAll::min(), min_cap);
|
|
|
|
|
if (max_exists)
|
|
|
|
|
writeNetLoad(net, MinMaxAll::max(), max_cap);
|
2020-07-06 01:37:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::writeNetLoad(const Net *net,
|
2020-07-06 01:37:50 +02:00
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
float cap) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_load ");
|
|
|
|
|
gzprintf(stream_, "%s ", minMaxFlag(min_max));
|
2020-07-06 01:37:50 +02:00
|
|
|
writeCapacitance(cap);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2020-07-06 01:37:50 +02:00
|
|
|
writeGetNet(net);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2020-07-06 01:37:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writePortLoads() const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_);
|
|
|
|
|
while (port_iter->hasNext()) {
|
|
|
|
|
Port *port = port_iter->next();
|
|
|
|
|
writePortLoads(port);
|
|
|
|
|
}
|
|
|
|
|
delete port_iter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::writePortLoads(const Port *port) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
const Corner *corner = corners_->findCorner(0); // missing corner arg
|
|
|
|
|
PortExtCap *ext_cap = sdc_->portExtCap(port, corner);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (ext_cap) {
|
|
|
|
|
WriteGetPort write_port(port, this);
|
|
|
|
|
writeRiseFallMinMaxCapCmd("set_load -pin_load",
|
|
|
|
|
ext_cap->pinCap(),
|
|
|
|
|
write_port);
|
|
|
|
|
writeRiseFallMinMaxCapCmd("set_load -wire_load",
|
|
|
|
|
ext_cap->wireCap(),
|
|
|
|
|
write_port);
|
|
|
|
|
writeMinMaxIntValuesCmd("set_port_fanout_number",
|
|
|
|
|
ext_cap->fanout(), write_port);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDriveResistances() const
|
|
|
|
|
{
|
|
|
|
|
CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_);
|
|
|
|
|
while (port_iter->hasNext()) {
|
|
|
|
|
Port *port = port_iter->next();
|
|
|
|
|
InputDrive *drive = sdc_->findInputDrive(port);
|
|
|
|
|
if (drive) {
|
2021-11-22 16:37:59 +01:00
|
|
|
for (auto rf : RiseFall::range()) {
|
|
|
|
|
if (drive->driveResistanceMinMaxEqual(rf)) {
|
2018-09-28 17:54:21 +02:00
|
|
|
float res;
|
|
|
|
|
bool exists;
|
2021-11-22 16:37:59 +01:00
|
|
|
drive->driveResistance(rf, MinMax::max(), res, exists);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_drive %s ",
|
2021-11-22 16:37:59 +01:00
|
|
|
transRiseFallFlag(rf));
|
2018-09-28 17:54:21 +02:00
|
|
|
writeResistance(res);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetPort(port);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto min_max : MinMax::range()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
float res;
|
|
|
|
|
bool exists;
|
2021-11-22 16:37:59 +01:00
|
|
|
drive->driveResistance(rf, min_max, res, exists);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (exists) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_drive %s %s ",
|
2021-11-22 16:37:59 +01:00
|
|
|
transRiseFallFlag(rf),
|
2019-07-18 15:19:00 +02:00
|
|
|
minMaxFlag(min_max));
|
2018-09-28 17:54:21 +02:00
|
|
|
writeResistance(res);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetPort(port);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete port_iter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDrivingCells() const
|
|
|
|
|
{
|
|
|
|
|
CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_);
|
|
|
|
|
while (port_iter->hasNext()) {
|
|
|
|
|
Port *port = port_iter->next();
|
|
|
|
|
InputDrive *drive = sdc_->findInputDrive(port);
|
|
|
|
|
if (drive) {
|
2019-11-11 23:30:19 +01:00
|
|
|
InputDriveCell *drive_rise_min = drive->driveCell(RiseFall::rise(),
|
2018-09-28 17:54:21 +02:00
|
|
|
MinMax::min());
|
2019-11-11 23:30:19 +01:00
|
|
|
InputDriveCell *drive_rise_max = drive->driveCell(RiseFall::rise(),
|
2018-09-28 17:54:21 +02:00
|
|
|
MinMax::max());
|
2019-11-11 23:30:19 +01:00
|
|
|
InputDriveCell *drive_fall_min = drive->driveCell(RiseFall::fall(),
|
2018-09-28 17:54:21 +02:00
|
|
|
MinMax::min());
|
2019-11-11 23:30:19 +01:00
|
|
|
InputDriveCell *drive_fall_max = drive->driveCell(RiseFall::fall(),
|
2018-09-28 17:54:21 +02:00
|
|
|
MinMax::max());
|
|
|
|
|
if (drive_rise_min
|
|
|
|
|
&& drive_rise_max
|
|
|
|
|
&& drive_fall_min
|
|
|
|
|
&& drive_fall_max
|
|
|
|
|
&& drive_rise_min->equal(drive_rise_max)
|
|
|
|
|
&& drive_rise_min->equal(drive_fall_min)
|
|
|
|
|
&& drive_rise_min->equal(drive_fall_max))
|
|
|
|
|
// Only write one set_driving_cell if possible.
|
2019-03-13 01:25:53 +01:00
|
|
|
writeDrivingCell(port, drive_rise_min, nullptr, nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
else {
|
|
|
|
|
if (drive_rise_min
|
|
|
|
|
&& drive_rise_max
|
|
|
|
|
&& drive_rise_min->equal(drive_rise_max))
|
2019-11-11 23:30:19 +01:00
|
|
|
writeDrivingCell(port, drive_rise_min, RiseFall::rise(), nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
else {
|
|
|
|
|
if (drive_rise_min)
|
2019-11-11 23:30:19 +01:00
|
|
|
writeDrivingCell(port, drive_rise_min, RiseFall::rise(),
|
2018-09-28 17:54:21 +02:00
|
|
|
MinMax::min());
|
|
|
|
|
if (drive_rise_max)
|
2019-11-11 23:30:19 +01:00
|
|
|
writeDrivingCell(port, drive_rise_max, RiseFall::rise(),
|
2018-09-28 17:54:21 +02:00
|
|
|
MinMax::max());
|
|
|
|
|
}
|
|
|
|
|
if (drive_fall_min
|
|
|
|
|
&& drive_fall_max
|
|
|
|
|
&& drive_fall_min->equal(drive_fall_max))
|
2019-11-11 23:30:19 +01:00
|
|
|
writeDrivingCell(port, drive_fall_min, RiseFall::fall(), nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
else {
|
|
|
|
|
if (drive_fall_min)
|
2019-11-11 23:30:19 +01:00
|
|
|
writeDrivingCell(port, drive_fall_min, RiseFall::fall(),
|
2018-09-28 17:54:21 +02:00
|
|
|
MinMax::min());
|
|
|
|
|
if (drive_fall_max)
|
2019-11-11 23:30:19 +01:00
|
|
|
writeDrivingCell(port, drive_fall_max, RiseFall::fall(),
|
2018-09-28 17:54:21 +02:00
|
|
|
MinMax::max());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete port_iter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDrivingCell(Port *port,
|
|
|
|
|
InputDriveCell *drive_cell,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFall *rf,
|
2018-09-28 17:54:21 +02:00
|
|
|
const MinMax *min_max) const
|
|
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
const LibertyCell *cell = drive_cell->cell();
|
|
|
|
|
const LibertyPort *from_port = drive_cell->fromPort();
|
|
|
|
|
const LibertyPort *to_port = drive_cell->toPort();
|
2018-09-28 17:54:21 +02:00
|
|
|
float *from_slews = drive_cell->fromSlews();
|
2023-01-19 19:23:45 +01:00
|
|
|
const LibertyLibrary *lib = drive_cell->library();
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_driving_cell");
|
2019-11-11 23:30:19 +01:00
|
|
|
if (rf)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " %s", transRiseFallFlag(rf));
|
2018-09-28 17:54:21 +02:00
|
|
|
if (min_max)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " %s", minMaxFlag(min_max));
|
2019-02-16 21:07:59 +01:00
|
|
|
// Only write -library if it was specified in the sdc.
|
|
|
|
|
if (lib)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -library %s", lib->name());
|
|
|
|
|
gzprintf(stream_, " -lib_cell %s", cell->name());
|
2018-09-28 17:54:21 +02:00
|
|
|
if (from_port)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -from_pin {%s}",
|
2018-09-28 17:54:21 +02:00
|
|
|
from_port->name());
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_,
|
2018-09-28 17:54:21 +02:00
|
|
|
" -pin {%s} -input_transition_rise ",
|
|
|
|
|
to_port->name());
|
2019-11-11 23:30:19 +01:00
|
|
|
writeTime(from_slews[RiseFall::riseIndex()]);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -input_transition_fall ");
|
2019-11-11 23:30:19 +01:00
|
|
|
writeTime(from_slews[RiseFall::fallIndex()]);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetPort(port);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeInputTransitions() const
|
|
|
|
|
{
|
|
|
|
|
CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_);
|
|
|
|
|
while (port_iter->hasNext()) {
|
|
|
|
|
Port *port = port_iter->next();
|
|
|
|
|
InputDrive *drive = sdc_->findInputDrive(port);
|
|
|
|
|
if (drive) {
|
2025-02-01 23:53:28 +01:00
|
|
|
const RiseFallMinMax *slews = drive->slews();
|
2018-09-28 17:54:21 +02:00
|
|
|
WriteGetPort write_port(port, this);
|
|
|
|
|
writeRiseFallMinMaxTimeCmd("set_input_transition", slews, write_port);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete port_iter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeNetResistances() const
|
|
|
|
|
{
|
|
|
|
|
NetSeq nets;
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [net, res] : sdc_->netResistances())
|
2018-09-28 17:54:21 +02:00
|
|
|
nets.push_back(net);
|
|
|
|
|
sort(nets, NetPathNameLess(sdc_network_));
|
2023-01-19 19:23:45 +01:00
|
|
|
for (const Net *net : nets) {
|
2018-09-28 17:54:21 +02:00
|
|
|
float min_res, max_res;
|
|
|
|
|
bool min_exists, max_exists;
|
|
|
|
|
sdc_->resistance(net, MinMax::min(), min_res, min_exists);
|
|
|
|
|
sdc_->resistance(net, MinMax::max(), max_res, max_exists);
|
|
|
|
|
if (min_exists && max_exists
|
|
|
|
|
&& min_res == max_res)
|
|
|
|
|
writeNetResistance(net, MinMaxAll::all(), min_res);
|
|
|
|
|
else {
|
|
|
|
|
if (min_exists)
|
|
|
|
|
writeNetResistance(net, MinMaxAll::min(), min_res);
|
|
|
|
|
if (max_exists)
|
|
|
|
|
writeNetResistance(net, MinMaxAll::max(), max_res);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::writeNetResistance(const Net *net,
|
2018-09-28 17:54:21 +02:00
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
float res) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_resistance ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeResistance(res);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s ", minMaxFlag(min_max));
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetNet(net);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeConstants() const
|
|
|
|
|
{
|
|
|
|
|
PinSeq pins;
|
|
|
|
|
sortedLogicValuePins(sdc_->logicValues(), pins);
|
2023-01-19 19:23:45 +01:00
|
|
|
for (const Pin *pin : pins)
|
2018-09-28 17:54:21 +02:00
|
|
|
writeConstant(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::writeConstant(const Pin *pin) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
const char *cmd = setConstantCmd(pin);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s ", cmd);
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(pin, false);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::setConstantCmd(const Pin *pin) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
LogicValue value;
|
|
|
|
|
bool exists;
|
|
|
|
|
sdc_->logicValue(pin, value, exists);
|
|
|
|
|
switch (value) {
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::zero:
|
2021-08-16 21:17:48 +02:00
|
|
|
return "set_logic_zero";
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::one:
|
2018-09-28 17:54:21 +02:00
|
|
|
return "set_logic_one";
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::unknown:
|
2018-09-28 17:54:21 +02:00
|
|
|
return "set_logic_dc";
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::rise:
|
|
|
|
|
case LogicValue::fall:
|
2018-09-28 17:54:21 +02:00
|
|
|
default:
|
2024-01-08 03:23:53 +01:00
|
|
|
report_->critical(1621, "illegal set_logic value");
|
2020-12-14 02:21:35 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeCaseAnalysis() const
|
|
|
|
|
{
|
|
|
|
|
PinSeq pins;
|
|
|
|
|
sortedLogicValuePins(sdc_->caseLogicValues(), pins);
|
2023-01-19 19:23:45 +01:00
|
|
|
for (const Pin *pin : pins)
|
2018-09-28 17:54:21 +02:00
|
|
|
writeCaseAnalysis(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::writeCaseAnalysis(const Pin *pin) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
const char *value_str = caseAnalysisValueStr(pin);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_case_analysis %s ", value_str);
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(pin, false);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::caseAnalysisValueStr(const Pin *pin) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
LogicValue value;
|
|
|
|
|
bool exists;
|
|
|
|
|
sdc_->caseLogicValue(pin, value, exists);
|
|
|
|
|
switch (value) {
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::zero:
|
2018-09-28 17:54:21 +02:00
|
|
|
return "0";
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::one:
|
2018-09-28 17:54:21 +02:00
|
|
|
return "1";
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::rise:
|
2018-09-28 17:54:21 +02:00
|
|
|
return "rising";
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::fall:
|
2018-09-28 17:54:21 +02:00
|
|
|
return "falling";
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::unknown:
|
2018-09-28 17:54:21 +02:00
|
|
|
default:
|
2024-01-08 03:23:53 +01:00
|
|
|
report_->critical(1622, "invalid set_case_analysis value");
|
2020-12-14 02:21:35 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::sortedLogicValuePins(LogicValueMap &value_map,
|
2018-09-28 17:54:21 +02:00
|
|
|
PinSeq &pins) const
|
|
|
|
|
{
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [pin, value] : value_map)
|
2023-01-19 19:23:45 +01:00
|
|
|
pins.push_back(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
// Sort pins.
|
|
|
|
|
sort(pins, PinPathNameLess(sdc_network_));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDeratings() const
|
|
|
|
|
{
|
|
|
|
|
DeratingFactorsGlobal *factors = sdc_->derating_factors_;
|
|
|
|
|
if (factors)
|
|
|
|
|
writeDerating(factors);
|
|
|
|
|
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [net, factors] : sdc_->net_derating_factors_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
WriteGetNet write_net(net, this);
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto early_late : EarlyLate::range()) {
|
2019-03-13 01:25:53 +01:00
|
|
|
writeDerating(factors, TimingDerateType::net_delay, early_late,
|
|
|
|
|
&write_net);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [inst, factors] : sdc_->inst_derating_factors_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
WriteGetInstance write_inst(inst, this);
|
|
|
|
|
writeDerating(factors, &write_inst);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [cell, factors] : sdc_->cell_derating_factors_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
WriteGetLibCell write_cell(cell, this);
|
|
|
|
|
writeDerating(factors, &write_cell);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDerating(DeratingFactorsGlobal *factors) const
|
|
|
|
|
{
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto early_late : EarlyLate::range()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
bool delay_is_one_value, check_is_one_value, net_is_one_value;
|
|
|
|
|
float delay_value, check_value, net_value;
|
2019-03-13 01:25:53 +01:00
|
|
|
factors->factors(TimingDerateType::cell_delay)->isOneValue(early_late,
|
2018-09-28 17:54:21 +02:00
|
|
|
delay_is_one_value,
|
|
|
|
|
delay_value);
|
2019-03-13 01:25:53 +01:00
|
|
|
factors->factors(TimingDerateType::net_delay)->isOneValue(early_late,
|
|
|
|
|
net_is_one_value,
|
|
|
|
|
net_value);
|
2018-09-28 17:54:21 +02:00
|
|
|
DeratingFactors *cell_check_factors =
|
2019-03-13 01:25:53 +01:00
|
|
|
factors->factors(TimingDerateType::cell_check);
|
2018-09-28 17:54:21 +02:00
|
|
|
cell_check_factors->isOneValue(early_late, check_is_one_value, check_value);
|
|
|
|
|
if (delay_is_one_value
|
|
|
|
|
&& net_is_one_value
|
|
|
|
|
&& delay_value == net_value
|
|
|
|
|
&& (!cell_check_factors->hasValue()
|
|
|
|
|
|| (check_is_one_value && check_value == 1.0))) {
|
|
|
|
|
if (delay_value != 1.0) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_timing_derate %s ", earlyLateFlag(early_late));
|
2018-09-28 17:54:21 +02:00
|
|
|
writeFloat(delay_value);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (int type_index = 0;
|
|
|
|
|
type_index < timing_derate_type_count;
|
|
|
|
|
type_index++) {
|
|
|
|
|
TimingDerateType type = static_cast<TimingDerateType>(type_index);
|
|
|
|
|
DeratingFactors *type_factors = factors->factors(type);
|
2019-03-13 01:25:53 +01:00
|
|
|
writeDerating(type_factors, type, early_late, nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDerating(DeratingFactorsCell *factors,
|
|
|
|
|
WriteSdcObject *write_obj) const
|
|
|
|
|
{
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto early_late : EarlyLate::range()) {
|
2022-01-14 00:29:38 +01:00
|
|
|
DeratingFactors *delay_factors=factors->factors(TimingDerateCellType::cell_delay);
|
2019-03-13 01:25:53 +01:00
|
|
|
writeDerating(delay_factors, TimingDerateType::cell_delay, early_late, write_obj);
|
2022-01-14 00:29:38 +01:00
|
|
|
DeratingFactors *check_factors=factors->factors(TimingDerateCellType::cell_check);
|
2019-03-13 01:25:53 +01:00
|
|
|
writeDerating(check_factors, TimingDerateType::cell_check, early_late, write_obj);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDerating(DeratingFactors *factors,
|
|
|
|
|
TimingDerateType type,
|
|
|
|
|
const MinMax *early_late,
|
|
|
|
|
WriteSdcObject *write_obj) const
|
|
|
|
|
{
|
|
|
|
|
const char *type_key = timingDerateTypeKeyword(type);
|
|
|
|
|
bool is_one_value;
|
|
|
|
|
float value;
|
|
|
|
|
factors->isOneValue(early_late, is_one_value, value);
|
|
|
|
|
if (is_one_value) {
|
|
|
|
|
if (value != 1.0) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_timing_derate %s %s ",
|
2018-09-28 17:54:21 +02:00
|
|
|
type_key,
|
|
|
|
|
earlyLateFlag(early_late));
|
|
|
|
|
writeFloat(value);
|
|
|
|
|
if (write_obj) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
write_obj->write();
|
|
|
|
|
}
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (int clk_data_index = 0;
|
|
|
|
|
clk_data_index < path_clk_or_data_count;
|
|
|
|
|
clk_data_index++) {
|
|
|
|
|
PathClkOrData clk_data = static_cast<PathClkOrData>(clk_data_index);
|
|
|
|
|
static const char *clk_data_keys[] = {"-clock", "-data"};
|
|
|
|
|
const char *clk_data_key = clk_data_keys[clk_data_index];
|
|
|
|
|
factors->isOneValue(clk_data, early_late, is_one_value, value);
|
|
|
|
|
if (is_one_value) {
|
|
|
|
|
if (value != 1.0) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_timing_derate %s %s %s ",
|
2018-09-28 17:54:21 +02:00
|
|
|
type_key,
|
|
|
|
|
earlyLateFlag(early_late),
|
|
|
|
|
clk_data_key);
|
|
|
|
|
writeFloat(value);
|
|
|
|
|
if (write_obj) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
write_obj->write();
|
|
|
|
|
}
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2021-11-22 16:37:59 +01:00
|
|
|
for (auto rf : RiseFall::range()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
float factor;
|
|
|
|
|
bool exists;
|
2021-11-22 16:37:59 +01:00
|
|
|
factors->factor(clk_data, rf, early_late, factor, exists);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (exists) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_timing_derate %s %s %s %s ",
|
2018-09-28 17:54:21 +02:00
|
|
|
type_key,
|
|
|
|
|
clk_data_key,
|
2021-11-22 16:37:59 +01:00
|
|
|
transRiseFallFlag(rf),
|
2018-09-28 17:54:21 +02:00
|
|
|
earlyLateFlag(early_late));
|
|
|
|
|
writeFloat(factor);
|
|
|
|
|
if (write_obj) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
write_obj->write();
|
|
|
|
|
}
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
timingDerateTypeKeyword(TimingDerateType type)
|
|
|
|
|
{
|
|
|
|
|
static const char *type_keys[] = {"-cell_delay","-cell_check","-net_delay"};
|
|
|
|
|
return type_keys[static_cast<int>(type)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2023-09-09 02:51:21 +02:00
|
|
|
void
|
|
|
|
|
WriteSdc::writeVoltages() const
|
|
|
|
|
{
|
|
|
|
|
float voltage_max, voltage_min;
|
|
|
|
|
bool exists_max, exists_min;
|
|
|
|
|
sdc_->voltage(MinMax::max(), voltage_max, exists_max);
|
|
|
|
|
if (exists_max) {
|
|
|
|
|
sdc_->voltage(MinMax::min(), voltage_min, exists_min);
|
|
|
|
|
if (exists_min)
|
|
|
|
|
gzprintf(stream_, "set_voltage -min %.3f %.3f\n",
|
|
|
|
|
voltage_min,
|
|
|
|
|
voltage_max);
|
|
|
|
|
else
|
|
|
|
|
gzprintf(stream_, "set_voltage %.3f\n", voltage_max);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto& [net, volts] : sdc_->net_voltage_map_) {
|
|
|
|
|
volts.value(MinMax::max(), voltage_max, exists_max);
|
2023-09-09 02:51:21 +02:00
|
|
|
if (exists_max) {
|
2024-06-27 22:57:58 +02:00
|
|
|
volts.value(MinMax::min(), voltage_min, exists_min);
|
2023-09-09 02:51:21 +02:00
|
|
|
if (exists_min)
|
|
|
|
|
gzprintf(stream_, "set_voltage -object_list %s -min %.3f %.3f\n",
|
|
|
|
|
sdc_network_->pathName(net),
|
|
|
|
|
voltage_min,
|
|
|
|
|
voltage_max);
|
|
|
|
|
else
|
|
|
|
|
gzprintf(stream_, "set_voltage -object_list %s %.3f\n",
|
|
|
|
|
sdc_network_->pathName(net),
|
|
|
|
|
voltage_max);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
void
|
|
|
|
|
WriteSdc::writeDesignRules() const
|
|
|
|
|
{
|
|
|
|
|
writeCommentSection("Design Rules");
|
|
|
|
|
writeMinPulseWidths();
|
|
|
|
|
writeLatchBorowLimits();
|
|
|
|
|
writeSlewLimits();
|
|
|
|
|
writeCapLimits();
|
|
|
|
|
writeFanoutLimits();
|
|
|
|
|
writeMaxArea();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeMinPulseWidths() const
|
|
|
|
|
{
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [pin, min_widths] : sdc_->pin_min_pulse_width_map_) {
|
2019-10-25 17:51:59 +02:00
|
|
|
WriteGetPin write_obj(pin, false, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeMinPulseWidths(min_widths, write_obj);
|
|
|
|
|
}
|
2023-01-19 19:23:45 +01:00
|
|
|
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [inst, min_widths] : sdc_->inst_min_pulse_width_map_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
WriteGetInstance write_obj(inst, this);
|
|
|
|
|
writeMinPulseWidths(min_widths, write_obj);
|
|
|
|
|
}
|
2023-01-19 19:23:45 +01:00
|
|
|
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [clk, min_widths] : sdc_->clk_min_pulse_width_map_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
WriteGetClock write_obj(clk, this);
|
|
|
|
|
writeMinPulseWidths(min_widths, write_obj);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeMinPulseWidths(RiseFallValues *min_widths,
|
|
|
|
|
WriteSdcObject &write_obj) const
|
|
|
|
|
{
|
|
|
|
|
bool hi_exists, low_exists;
|
|
|
|
|
float hi, low;
|
2019-11-11 23:30:19 +01:00
|
|
|
min_widths->value(RiseFall::rise(), hi, hi_exists);
|
|
|
|
|
min_widths->value(RiseFall::fall(), low, low_exists);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (hi_exists && low_exists
|
|
|
|
|
&& hi == low)
|
|
|
|
|
writeMinPulseWidth("", hi, write_obj);
|
|
|
|
|
else {
|
|
|
|
|
if (hi_exists)
|
|
|
|
|
writeMinPulseWidth("-high ", hi, write_obj);
|
|
|
|
|
if (low_exists)
|
|
|
|
|
writeMinPulseWidth("-low ", low, write_obj);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeMinPulseWidth(const char *hi_low,
|
|
|
|
|
float value,
|
|
|
|
|
WriteSdcObject &write_obj) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_min_pulse_width %s", hi_low);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeTime(value);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
write_obj.write();
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeLatchBorowLimits() const
|
|
|
|
|
{
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [pin, limit] : sdc_->pin_latch_borrow_limit_map_) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_max_time_borrow ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeTime(limit);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(pin, false);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2023-01-19 19:23:45 +01:00
|
|
|
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [inst, limit] : sdc_->inst_latch_borrow_limit_map_) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_max_time_borrow ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeTime(limit);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetInstance(inst);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2023-01-19 19:23:45 +01:00
|
|
|
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [clk, limit] : sdc_->clk_latch_borrow_limit_map_) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_max_time_borrow ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeTime(limit);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetClock(clk);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeSlewLimits() const
|
|
|
|
|
{
|
|
|
|
|
const MinMax *min_max = MinMax::max();
|
|
|
|
|
float slew;
|
|
|
|
|
bool exists;
|
|
|
|
|
sdc_->slewLimit(cell_, min_max, slew, exists);
|
|
|
|
|
if (exists) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_max_transition ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeTime(slew);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " [current_design]\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_);
|
|
|
|
|
while (port_iter->hasNext()) {
|
|
|
|
|
Port *port = port_iter->next();
|
|
|
|
|
sdc_->slewLimit(port, min_max, slew, exists);
|
|
|
|
|
if (exists) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_max_transition ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeTime(slew);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetPort(port);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete port_iter;
|
|
|
|
|
|
|
|
|
|
writeClkSlewLimits();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClkSlewLimits() const
|
|
|
|
|
{
|
|
|
|
|
const MinMax *min_max = MinMax::max();
|
|
|
|
|
ClockSeq clks;
|
|
|
|
|
sdc_->sortedClocks(clks);
|
2023-01-19 19:23:45 +01:00
|
|
|
for (const Clock *clk : clks) {
|
2018-09-28 17:54:21 +02:00
|
|
|
float rise_clk_limit, fall_clk_limit, rise_data_limit, fall_data_limit;
|
|
|
|
|
bool rise_clk_exists, fall_clk_exists, rise_data_exists, fall_data_exists;
|
2019-11-11 23:30:19 +01:00
|
|
|
clk->slewLimit(RiseFall::rise(), PathClkOrData::clk, min_max,
|
2018-09-28 17:54:21 +02:00
|
|
|
rise_clk_limit, rise_clk_exists);
|
2019-11-11 23:30:19 +01:00
|
|
|
clk->slewLimit(RiseFall::fall(), PathClkOrData::clk, min_max,
|
2018-09-28 17:54:21 +02:00
|
|
|
fall_clk_limit, fall_clk_exists);
|
2019-11-11 23:30:19 +01:00
|
|
|
clk->slewLimit(RiseFall::rise(), PathClkOrData::data, min_max,
|
2018-09-28 17:54:21 +02:00
|
|
|
rise_data_limit, rise_data_exists);
|
2019-11-11 23:30:19 +01:00
|
|
|
clk->slewLimit(RiseFall::fall(), PathClkOrData::data, min_max,
|
2018-09-28 17:54:21 +02:00
|
|
|
fall_data_limit, fall_data_exists);
|
|
|
|
|
if (rise_clk_exists && fall_clk_exists
|
|
|
|
|
&& rise_data_exists && fall_data_exists
|
|
|
|
|
&& fall_clk_limit == rise_clk_limit
|
|
|
|
|
&& rise_data_limit == rise_clk_limit
|
|
|
|
|
&& fall_data_limit == rise_clk_limit)
|
|
|
|
|
writeClkSlewLimit("", "", clk, rise_clk_limit);
|
|
|
|
|
else {
|
|
|
|
|
if (rise_clk_exists && fall_clk_exists
|
|
|
|
|
&& fall_clk_limit == rise_clk_limit)
|
|
|
|
|
writeClkSlewLimit("-clock_path ", "", clk, rise_clk_limit);
|
|
|
|
|
else {
|
|
|
|
|
if (rise_clk_exists)
|
|
|
|
|
writeClkSlewLimit("-clock_path ", "-rise ", clk, rise_clk_limit);
|
|
|
|
|
if (fall_clk_exists)
|
|
|
|
|
writeClkSlewLimit("-clock_path ", "-fall ", clk, fall_clk_limit);
|
|
|
|
|
}
|
|
|
|
|
if (rise_data_exists && fall_data_exists
|
|
|
|
|
&& fall_data_limit == rise_data_limit)
|
|
|
|
|
writeClkSlewLimit("-data_path ", "", clk, rise_data_limit);
|
|
|
|
|
else {
|
|
|
|
|
if (rise_data_exists)
|
|
|
|
|
writeClkSlewLimit("-data_path ", "-rise ", clk, rise_data_limit);
|
|
|
|
|
if (fall_data_exists) {
|
|
|
|
|
writeClkSlewLimit("-data_path ", "-fall ", clk, fall_data_limit);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClkSlewLimit(const char *clk_data,
|
|
|
|
|
const char *rise_fall,
|
|
|
|
|
const Clock *clk,
|
|
|
|
|
float limit) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_max_transition %s%s", clk_data, rise_fall);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeTime(limit);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetClock(clk);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeCapLimits() const
|
|
|
|
|
{
|
|
|
|
|
writeCapLimits(MinMax::min(), "set_min_capacitance");
|
|
|
|
|
writeCapLimits(MinMax::max(), "set_max_capacitance");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeCapLimits(const MinMax *min_max,
|
|
|
|
|
const char *cmd) const
|
|
|
|
|
{
|
|
|
|
|
float cap;
|
|
|
|
|
bool exists;
|
|
|
|
|
sdc_->capacitanceLimit(cell_, min_max, cap, exists);
|
|
|
|
|
if (exists) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s ", cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeCapacitance(cap);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " [current_design]\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [port, limits] : sdc_->port_cap_limit_map_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
float cap;
|
|
|
|
|
bool exists;
|
2024-06-27 22:57:58 +02:00
|
|
|
limits.value(min_max, cap, exists);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (exists) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s ", cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeCapacitance(cap);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetPort(port);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-27 22:57:58 +02:00
|
|
|
for (const auto [pin, limits] : sdc_->pin_cap_limit_map_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
float cap;
|
|
|
|
|
bool exists;
|
2024-06-27 22:57:58 +02:00
|
|
|
limits.value(min_max, cap, exists);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (exists) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s ", cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeCapacitance(cap);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(pin, false);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeMaxArea() const
|
|
|
|
|
{
|
|
|
|
|
float max_area = sdc_->maxArea();
|
|
|
|
|
if (max_area > 0.0) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set_max_area ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeFloat(max_area);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeFanoutLimits() const
|
|
|
|
|
{
|
|
|
|
|
writeFanoutLimits(MinMax::min(), "set_min_fanout");
|
|
|
|
|
writeFanoutLimits(MinMax::max(), "set_max_fanout");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeFanoutLimits(const MinMax *min_max,
|
|
|
|
|
const char *cmd) const
|
|
|
|
|
{
|
|
|
|
|
float fanout;
|
|
|
|
|
bool exists;
|
|
|
|
|
sdc_->fanoutLimit(cell_, min_max, fanout, exists);
|
|
|
|
|
if (exists) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s ", cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeFloat(fanout);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " [current_design]\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_);
|
|
|
|
|
while (port_iter->hasNext()) {
|
|
|
|
|
Port *port = port_iter->next();
|
|
|
|
|
sdc_->fanoutLimit(port, min_max, fanout, exists);
|
|
|
|
|
if (exists) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s ", cmd);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeFloat(fanout);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetPort(port);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete port_iter;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeVariables() const
|
|
|
|
|
{
|
2025-04-10 01:35:15 +02:00
|
|
|
if (variables_->propagateAllClocks()) {
|
2019-10-25 17:51:59 +02:00
|
|
|
if (native_)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set sta_propagate_all_clocks 1\n");
|
2019-10-25 17:51:59 +02:00
|
|
|
else
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set timing_all_clocks_propagated true\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2025-04-10 01:35:15 +02:00
|
|
|
if (variables_->presetClrArcsEnabled()) {
|
2019-10-25 17:51:59 +02:00
|
|
|
if (native_)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set sta_preset_clear_arcs_enabled 1\n");
|
2019-10-25 17:51:59 +02:00
|
|
|
else
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "set timing_enable_preset_clear_arcs true\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::writeGetTimingArcsOfOjbects(const LibertyCell *cell) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[%s -of_objects ", getTimingArcsCmd());
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetLibCell(cell);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "]");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetTimingArcs(Edge *edge) const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
writeGetTimingArcs(edge, nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetTimingArcs(Edge *edge,
|
|
|
|
|
const char *filter) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[%s -from ", getTimingArcsCmd());
|
2018-09-28 17:54:21 +02:00
|
|
|
Vertex *from_vertex = edge->from(graph_);
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(from_vertex->pin(), true);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -to ");
|
2018-09-28 17:54:21 +02:00
|
|
|
Vertex *to_vertex = edge->to(graph_);
|
2019-10-25 17:51:59 +02:00
|
|
|
writeGetPin(to_vertex->pin(), false);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (filter)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -filter {%s}", filter);
|
|
|
|
|
gzprintf(stream_, "]");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
WriteSdc::getTimingArcsCmd() const
|
|
|
|
|
{
|
2019-10-25 17:51:59 +02:00
|
|
|
return native_ ? "get_timing_edges" : "get_timing_arcs";
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetLibCell(const LibertyCell *cell) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[get_lib_cells {%s/%s}]",
|
2018-09-28 17:54:21 +02:00
|
|
|
cell->libertyLibrary()->name(),
|
|
|
|
|
cell->name());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetLibPin(const LibertyPort *port) const
|
|
|
|
|
{
|
|
|
|
|
LibertyCell *cell = port->libertyCell();
|
|
|
|
|
LibertyLibrary *lib = cell->libertyLibrary();
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[get_lib_pins {%s/%s/%s}]",
|
2018-09-28 17:54:21 +02:00
|
|
|
lib->name(),
|
|
|
|
|
cell->name(),
|
|
|
|
|
port->name());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetClocks(ClockSet *clks) const
|
|
|
|
|
{
|
|
|
|
|
bool first = true;
|
|
|
|
|
bool multiple = clks->size() > 1;
|
|
|
|
|
if (multiple)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[list ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetClocks(clks, multiple, first);
|
|
|
|
|
if (multiple)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "]");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetClocks(ClockSet *clks,
|
|
|
|
|
bool multiple,
|
|
|
|
|
bool &first) const
|
|
|
|
|
{
|
2023-01-19 19:23:45 +01:00
|
|
|
ClockSeq clks1 = sortByName(clks);
|
|
|
|
|
for (const Clock *clk : clks1) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (multiple && !first)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\\\n ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetClock(clk);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetClock(const Clock *clk) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[get_clocks {%s}]",
|
2018-09-28 17:54:21 +02:00
|
|
|
clk->name());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetPort(const Port *port) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[get_ports {%s}]", sdc_network_->name(port));
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-01-19 19:23:45 +01:00
|
|
|
WriteSdc::writeGetPins(const PinSet *pins,
|
2019-10-25 17:51:59 +02:00
|
|
|
bool map_hpin_to_drvr) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2020-01-05 20:35:51 +01:00
|
|
|
if (map_hpins_) {
|
2023-01-19 19:23:45 +01:00
|
|
|
PinSet leaf_pins(network_);;
|
|
|
|
|
for (const Pin *pin : *pins) {
|
2020-01-05 20:35:51 +01:00
|
|
|
if (network_->isHierarchical(pin)) {
|
|
|
|
|
if (map_hpin_to_drvr)
|
|
|
|
|
findLeafDriverPins(const_cast<Pin*>(pin), network_, &leaf_pins);
|
|
|
|
|
else
|
|
|
|
|
findLeafLoadPins(const_cast<Pin*>(pin), network_, &leaf_pins);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
leaf_pins.insert(pin);
|
|
|
|
|
}
|
2023-01-19 19:23:45 +01:00
|
|
|
PinSeq pins1 = sortByPathName(&leaf_pins, sdc_network_);
|
|
|
|
|
writeGetPins1(&pins1);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
PinSeq pins1 = sortByPathName(pins, sdc_network_);
|
|
|
|
|
writeGetPins1(&pins1);
|
2020-01-05 20:35:51 +01:00
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2020-01-05 20:35:51 +01:00
|
|
|
WriteSdc::writeGetPins1(PinSeq *pins) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
bool multiple = pins->size() > 1;
|
|
|
|
|
if (multiple)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[list ");
|
2018-09-28 17:54:21 +02:00
|
|
|
bool first = true;
|
2023-01-19 19:23:45 +01:00
|
|
|
for (const Pin *pin : *pins) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (multiple && !first)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\\\n ");
|
2020-01-05 20:35:51 +01:00
|
|
|
writeGetPin(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
if (multiple)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "]");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetPin(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
if (sdc_network_->instance(pin) == instance_)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[get_ports {%s}]", sdc_network_->portName(pin));
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[get_pins {%s}]", pathName(pin));
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-25 17:51:59 +02:00
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetPin(const Pin *pin,
|
|
|
|
|
bool map_hpin_to_drvr) const
|
|
|
|
|
{
|
2020-01-05 20:35:51 +01:00
|
|
|
if (map_hpins_ && network_->isHierarchical(pin)) {
|
2023-01-19 19:23:45 +01:00
|
|
|
PinSet pins(network_);
|
2020-01-05 20:35:51 +01:00
|
|
|
pins.insert(const_cast<Pin*>(pin));
|
|
|
|
|
writeGetPins(&pins, map_hpin_to_drvr);
|
2019-10-25 17:51:59 +02:00
|
|
|
}
|
2020-01-05 20:35:51 +01:00
|
|
|
else
|
|
|
|
|
writeGetPin(pin);
|
2019-10-25 17:51:59 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetNet(const Net *net) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[get_nets {%s}]", pathName(net));
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetInstance(const Instance *inst) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "[get_cells {%s}]", pathName(inst));
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
WriteSdc::pathName(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
const char *pin_path = sdc_network_->pathName(pin);
|
|
|
|
|
if (top_instance_)
|
|
|
|
|
return pin_path;
|
|
|
|
|
else
|
|
|
|
|
return pin_path + instance_name_length_ + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
WriteSdc::pathName(const Net *net) const
|
|
|
|
|
{
|
|
|
|
|
const char *net_path = sdc_network_->pathName(net);
|
|
|
|
|
if (top_instance_)
|
|
|
|
|
return net_path;
|
|
|
|
|
else
|
|
|
|
|
return net_path + instance_name_length_ + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
WriteSdc::pathName(const Instance *inst) const
|
|
|
|
|
{
|
|
|
|
|
const char *inst_path = sdc_network_->pathName(inst);
|
|
|
|
|
if (top_instance_)
|
|
|
|
|
return inst_path;
|
|
|
|
|
else
|
|
|
|
|
return inst_path + instance_name_length_ + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeCommentSection(const char *line) const
|
|
|
|
|
{
|
|
|
|
|
writeCommentSeparator();
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "# %s\n", line);
|
2018-09-28 17:54:21 +02:00
|
|
|
writeCommentSeparator();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeCommentSeparator() const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "###############################################################################\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeRiseFallMinMaxTimeCmd(const char *sdc_cmd,
|
2023-01-19 19:23:45 +01:00
|
|
|
const RiseFallMinMax *values,
|
2018-09-28 17:54:21 +02:00
|
|
|
WriteSdcObject &write_object) const
|
|
|
|
|
{
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, values, units_->timeUnit()->scale(),
|
|
|
|
|
write_object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeRiseFallMinMaxCapCmd(const char *sdc_cmd,
|
2023-01-19 19:23:45 +01:00
|
|
|
const RiseFallMinMax *values,
|
2018-09-28 17:54:21 +02:00
|
|
|
WriteSdcObject &write_object) const
|
|
|
|
|
{
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, values, units_->capacitanceUnit()->scale(),
|
|
|
|
|
write_object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd,
|
2023-01-19 19:23:45 +01:00
|
|
|
const RiseFallMinMax *values,
|
2018-09-28 17:54:21 +02:00
|
|
|
float scale,
|
|
|
|
|
WriteSdcObject &write_object) const
|
|
|
|
|
{
|
|
|
|
|
float fall_min, fall_max, rise_min, rise_max;
|
|
|
|
|
bool fall_min_exists, fall_max_exists, rise_min_exists, rise_max_exists;
|
2019-11-11 23:30:19 +01:00
|
|
|
values->value(RiseFall::fall(), MinMax::min(),
|
2018-09-28 17:54:21 +02:00
|
|
|
fall_min, fall_min_exists);
|
2019-11-11 23:30:19 +01:00
|
|
|
values->value(RiseFall::fall(), MinMax::max(),
|
2018-09-28 17:54:21 +02:00
|
|
|
fall_max, fall_max_exists);
|
2019-11-11 23:30:19 +01:00
|
|
|
values->value(RiseFall::rise(), MinMax::min(),
|
2018-09-28 17:54:21 +02:00
|
|
|
rise_min, rise_min_exists);
|
2019-11-11 23:30:19 +01:00
|
|
|
values->value(RiseFall::rise(), MinMax::max(),
|
2018-09-28 17:54:21 +02:00
|
|
|
rise_max, rise_max_exists);
|
|
|
|
|
if (fall_min_exists && fall_max_exists
|
|
|
|
|
&& rise_min_exists && rise_max_exists) {
|
|
|
|
|
if (fall_min == rise_min
|
|
|
|
|
&& rise_max == rise_min
|
|
|
|
|
&& fall_max == rise_min) {
|
|
|
|
|
// rise/fall/min/max match.
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::riseFall(), MinMaxAll::all(),
|
2018-09-28 17:54:21 +02:00
|
|
|
write_object);
|
|
|
|
|
}
|
|
|
|
|
else if (rise_min == fall_min
|
|
|
|
|
&& rise_max == fall_max) {
|
|
|
|
|
// rise/fall match.
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::riseFall(), MinMaxAll::min(),
|
2018-09-28 17:54:21 +02:00
|
|
|
write_object);
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, rise_max, scale,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::riseFall(), MinMaxAll::max(),
|
2018-09-28 17:54:21 +02:00
|
|
|
write_object);
|
|
|
|
|
}
|
|
|
|
|
else if (rise_min == rise_max
|
|
|
|
|
&& fall_min == fall_max) {
|
|
|
|
|
// min/max match.
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::rise(), MinMaxAll::all(),
|
2018-09-28 17:54:21 +02:00
|
|
|
write_object);
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, fall_min, scale,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::fall(), MinMaxAll::all(),
|
2018-09-28 17:54:21 +02:00
|
|
|
write_object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (rise_min_exists)
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::rise(), MinMaxAll::min(),
|
2018-09-28 17:54:21 +02:00
|
|
|
write_object);
|
|
|
|
|
if (rise_max_exists)
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, rise_max, scale,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::rise(), MinMaxAll::max(),
|
2018-09-28 17:54:21 +02:00
|
|
|
write_object);
|
|
|
|
|
if (fall_min_exists)
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, fall_min, scale,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::fall(), MinMaxAll::min(),
|
2018-09-28 17:54:21 +02:00
|
|
|
write_object);
|
|
|
|
|
if (fall_max_exists)
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, fall_max, scale,
|
2019-11-11 23:30:19 +01:00
|
|
|
RiseFallBoth::fall(), MinMaxAll::max(),
|
2018-09-28 17:54:21 +02:00
|
|
|
write_object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd,
|
|
|
|
|
float value,
|
|
|
|
|
float scale,
|
2019-11-11 23:30:19 +01:00
|
|
|
const RiseFallBoth *rf,
|
2018-09-28 17:54:21 +02:00
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
WriteSdcObject &write_object) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s%s%s ",
|
2018-09-28 17:54:21 +02:00
|
|
|
sdc_cmd,
|
2019-11-11 23:30:19 +01:00
|
|
|
transRiseFallFlag(rf),
|
2018-09-28 17:54:21 +02:00
|
|
|
minMaxFlag(min_max));
|
|
|
|
|
writeFloat(value / scale);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
write_object.write();
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockKey(const Clock *clk) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -clock ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeGetClock(clk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeMinMaxFloatValuesCmd(const char *sdc_cmd,
|
|
|
|
|
MinMaxFloatValues *values,
|
|
|
|
|
float scale,
|
|
|
|
|
WriteSdcObject &write_object) const
|
|
|
|
|
{
|
|
|
|
|
float min, max;
|
|
|
|
|
bool min_exists, max_exists;
|
|
|
|
|
values->value(MinMax::min(), min, min_exists);
|
|
|
|
|
values->value(MinMax::max(), max, max_exists);
|
|
|
|
|
if (min_exists && max_exists
|
|
|
|
|
&& min == max) {
|
|
|
|
|
// min/max match.
|
|
|
|
|
writeMinMaxFloatCmd(sdc_cmd, min, scale, MinMaxAll::all(), write_object);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (min_exists)
|
|
|
|
|
writeMinMaxFloatCmd(sdc_cmd, min, scale, MinMaxAll::min(), write_object);
|
|
|
|
|
if (max_exists)
|
|
|
|
|
writeMinMaxFloatCmd(sdc_cmd, max, scale, MinMaxAll::max(), write_object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeMinMaxFloatCmd(const char *sdc_cmd,
|
|
|
|
|
float value,
|
|
|
|
|
float scale,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
WriteSdcObject &write_object) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s%s ",
|
2018-09-28 17:54:21 +02:00
|
|
|
sdc_cmd,
|
|
|
|
|
minMaxFlag(min_max));
|
|
|
|
|
writeFloat(value / scale);
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
write_object.write();
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeMinMaxIntValuesCmd(const char *sdc_cmd,
|
|
|
|
|
MinMaxIntValues *values,
|
|
|
|
|
WriteSdcObject &write_object) const
|
|
|
|
|
{
|
|
|
|
|
int min, max;
|
|
|
|
|
bool min_exists, max_exists;
|
|
|
|
|
values->value(MinMax::min(), min, min_exists);
|
|
|
|
|
values->value(MinMax::max(), max, max_exists);
|
|
|
|
|
if (min_exists && max_exists
|
|
|
|
|
&& min == max) {
|
|
|
|
|
// min/max match.
|
|
|
|
|
writeMinMaxIntCmd(sdc_cmd, min, MinMaxAll::all(), write_object);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (min_exists)
|
|
|
|
|
writeMinMaxIntCmd(sdc_cmd, min, MinMaxAll::min(), write_object);
|
|
|
|
|
if (max_exists)
|
|
|
|
|
writeMinMaxIntCmd(sdc_cmd, max, MinMaxAll::max(), write_object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeMinMaxIntCmd(const char *sdc_cmd,
|
|
|
|
|
int value,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
WriteSdcObject &write_object) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%s%s ",
|
2018-09-28 17:54:21 +02:00
|
|
|
sdc_cmd,
|
|
|
|
|
minMaxFlag(min_max));
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%d ", value);
|
2018-09-28 17:54:21 +02:00
|
|
|
write_object.write();
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
WriteSdc::scaleTime(float time) const
|
|
|
|
|
{
|
|
|
|
|
return time / units_->timeUnit()->scale();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
WriteSdc::scaleCapacitance(float cap) const
|
|
|
|
|
{
|
|
|
|
|
return cap / units_->capacitanceUnit()->scale();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
WriteSdc::scaleResistance(float res) const
|
|
|
|
|
{
|
|
|
|
|
return res / units_->resistanceUnit()->scale();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeFloat(float value) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%.*f", digits_, value);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeTime(float time) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%.*f", digits_, scaleTime(time));
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeCapacitance(float cap) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%.*f", digits_, scaleCapacitance(cap));
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeResistance(float res) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "%.*f", digits_, scaleResistance(res));
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeFloatSeq(FloatSeq *floats,
|
|
|
|
|
float scale) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "{");
|
2018-09-28 17:54:21 +02:00
|
|
|
bool first = true;
|
2023-01-19 19:23:45 +01:00
|
|
|
for (float flt : *floats) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!first)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
2018-09-28 17:54:21 +02:00
|
|
|
writeFloat(flt * scale);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "}");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeIntSeq(IntSeq *ints) const
|
|
|
|
|
{
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "{");
|
2018-09-28 17:54:21 +02:00
|
|
|
bool first = true;
|
2023-01-19 19:23:45 +01:00
|
|
|
for (int i : *ints) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!first)
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " ");
|
|
|
|
|
gzprintf(stream_, "%d", i);
|
2018-09-28 17:54:21 +02:00
|
|
|
first = false;
|
|
|
|
|
}
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, "}");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
static const char *
|
2019-11-11 23:30:19 +01:00
|
|
|
transRiseFallFlag(const RiseFall *rf)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
return (rf == RiseFall::rise()) ? "-rise" : "-fall";
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
2019-11-11 23:30:19 +01:00
|
|
|
transRiseFallFlag(const RiseFallBoth *rf)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2019-11-11 23:30:19 +01:00
|
|
|
if (rf == RiseFallBoth::rise())
|
2018-09-28 17:54:21 +02:00
|
|
|
return " -rise";
|
2019-11-11 23:30:19 +01:00
|
|
|
else if (rf == RiseFallBoth::fall())
|
2018-09-28 17:54:21 +02:00
|
|
|
return " -fall";
|
2020-12-14 02:21:35 +01:00
|
|
|
else
|
2018-09-28 17:54:21 +02:00
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
minMaxFlag(const MinMaxAll *min_max)
|
|
|
|
|
{
|
2020-12-14 02:21:35 +01:00
|
|
|
if (min_max == MinMaxAll::min())
|
2018-09-28 17:54:21 +02:00
|
|
|
return " -min";
|
|
|
|
|
else if (min_max == MinMaxAll::max())
|
|
|
|
|
return " -max";
|
2020-12-14 02:21:35 +01:00
|
|
|
else
|
|
|
|
|
return "";
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
minMaxFlag(const MinMax *min_max)
|
|
|
|
|
{
|
2020-12-14 02:21:35 +01:00
|
|
|
return (min_max == MinMax::min()) ? " -min" : " -max";
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
earlyLateFlag(const MinMax *early_late)
|
|
|
|
|
{
|
2020-12-14 02:21:35 +01:00
|
|
|
return (early_late == MinMax::min()) ? "-early" : "-late";
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeSetupHoldFlag(const MinMaxAll *min_max) const
|
|
|
|
|
{
|
|
|
|
|
if (min_max == MinMaxAll::min())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -hold");
|
2018-09-28 17:54:21 +02:00
|
|
|
else if (min_max == MinMaxAll::max())
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -setup");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
setupHoldFlag(const MinMax *min_max)
|
|
|
|
|
{
|
2020-12-14 02:21:35 +01:00
|
|
|
return (min_max == MinMax::min()) ? " -hold" : " -setup";
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeCmdComment(SdcCmdComment *cmd) const
|
|
|
|
|
{
|
|
|
|
|
const char *comment = cmd->comment();
|
|
|
|
|
if (comment) {
|
2021-11-15 15:31:29 +01:00
|
|
|
gzprintf(stream_, " -comment {%s}", comment);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|