2018-09-28 17:54:21 +02:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2019-01-01 21:26:11 +01:00
|
|
|
// Copyright (c) 2019, Parallax Software, Inc.
|
2018-09-28 17:54:21 +02:00
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <time.h>
|
|
|
|
|
#include "Machine.hh"
|
|
|
|
|
#include "DisallowCopyAssign.hh"
|
|
|
|
|
#include "Report.hh"
|
|
|
|
|
#include "Error.hh"
|
|
|
|
|
#include "Units.hh"
|
|
|
|
|
#include "StaState.hh"
|
|
|
|
|
#include "PortDirection.hh"
|
|
|
|
|
#include "RiseFallValues.hh"
|
|
|
|
|
#include "Transition.hh"
|
|
|
|
|
#include "Liberty.hh"
|
|
|
|
|
#include "Network.hh"
|
|
|
|
|
#include "NetworkCmp.hh"
|
|
|
|
|
#include "PortDelay.hh"
|
|
|
|
|
#include "ExceptionPath.hh"
|
|
|
|
|
#include "PortExtCap.hh"
|
|
|
|
|
#include "DisabledPorts.hh"
|
|
|
|
|
#include "Wireload.hh"
|
|
|
|
|
#include "ClockGroups.hh"
|
|
|
|
|
#include "ClockInsertion.hh"
|
|
|
|
|
#include "ClockLatency.hh"
|
|
|
|
|
#include "InputDrive.hh"
|
|
|
|
|
#include "DataCheck.hh"
|
|
|
|
|
#include "DeratingFactors.hh"
|
|
|
|
|
#include "Sdc.hh"
|
|
|
|
|
#include "Graph.hh"
|
|
|
|
|
#include "GraphCmp.hh"
|
|
|
|
|
#include "WriteSdc.hh"
|
|
|
|
|
#include "WriteSdcPvt.hh"
|
|
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
|
|
|
|
typedef Set<ClockSense*> ClockSenseSet;
|
|
|
|
|
typedef Vector<ClockSense*> ClockSenseSeq;
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
transRiseFallFlag(const TransRiseFall *tr);
|
|
|
|
|
static const char *
|
|
|
|
|
transRiseFallFlag(const TransRiseFallBoth *tr);
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(WriteSdcObject);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class WriteGetPort : public WriteSdcObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WriteGetPort(const Port *port,
|
|
|
|
|
const WriteSdc *writer);
|
|
|
|
|
virtual void write() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(WriteGetPort);
|
|
|
|
|
|
|
|
|
|
const Port *port_;
|
|
|
|
|
const WriteSdc *writer_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
WriteGetPort::WriteGetPort(const Port *port,
|
|
|
|
|
const WriteSdc *writer) :
|
|
|
|
|
port_(port),
|
|
|
|
|
writer_(writer)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteGetPort::write() const
|
|
|
|
|
{
|
|
|
|
|
writer_->writeGetPort(port_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class WriteGetPinAndClkKey : public WriteSdcObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WriteGetPinAndClkKey(const Pin *pin,
|
|
|
|
|
const Clock *clk,
|
|
|
|
|
const WriteSdc *writer);
|
|
|
|
|
virtual void write() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(WriteGetPinAndClkKey);
|
|
|
|
|
|
|
|
|
|
const Pin *pin_;
|
|
|
|
|
const Clock *clk_;
|
|
|
|
|
const WriteSdc *writer_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
WriteGetPinAndClkKey::WriteGetPinAndClkKey(const Pin *pin,
|
|
|
|
|
const Clock *clk,
|
|
|
|
|
const WriteSdc *writer) :
|
|
|
|
|
pin_(pin),
|
|
|
|
|
clk_(clk),
|
|
|
|
|
writer_(writer)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteGetPinAndClkKey::write() const
|
|
|
|
|
{
|
|
|
|
|
writer_->writeClockKey(clk_);
|
|
|
|
|
fprintf(writer_->stream(), " ");
|
|
|
|
|
writer_->writeGetPin(pin_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class WriteGetPin : public WriteSdcObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WriteGetPin(const Pin *pin,
|
|
|
|
|
const WriteSdc *writer);
|
|
|
|
|
virtual void write() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(WriteGetPin);
|
|
|
|
|
|
|
|
|
|
const Pin *pin_;
|
|
|
|
|
const WriteSdc *writer_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
WriteGetPin::WriteGetPin(const Pin *pin,
|
|
|
|
|
const WriteSdc *writer) :
|
|
|
|
|
pin_(pin),
|
|
|
|
|
writer_(writer)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteGetPin::write() const
|
|
|
|
|
{
|
|
|
|
|
writer_->writeGetPin(pin_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class WriteGetNet : public WriteSdcObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
WriteGetNet(const Net *net,
|
|
|
|
|
const WriteSdc *writer);
|
|
|
|
|
virtual void write() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(WriteGetNet);
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(WriteGetInstance);
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(WriteGetLibCell);
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(WriteGetClock);
|
|
|
|
|
|
|
|
|
|
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-02-16 21:07:59 +01:00
|
|
|
bool compatible,
|
2018-09-28 17:54:21 +02:00
|
|
|
bool no_timestamp,
|
|
|
|
|
int digits,
|
|
|
|
|
Sdc *sdc)
|
|
|
|
|
{
|
2019-02-16 21:07:59 +01:00
|
|
|
WriteSdc writer(instance, filename, creator, compatible, digits,
|
2018-09-28 17:54:21 +02:00
|
|
|
no_timestamp, sdc);
|
|
|
|
|
writer.write();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WriteSdc::WriteSdc(Instance *instance,
|
|
|
|
|
const char *filename,
|
|
|
|
|
const char *creator,
|
2019-02-16 21:07:59 +01:00
|
|
|
bool compatible,
|
2018-09-28 17:54:21 +02:00
|
|
|
int digits,
|
|
|
|
|
bool no_timestamp,
|
|
|
|
|
Sdc *sdc) :
|
|
|
|
|
StaState(sdc),
|
|
|
|
|
instance_(instance),
|
|
|
|
|
filename_(filename),
|
|
|
|
|
creator_(creator),
|
2019-02-16 21:07:59 +01:00
|
|
|
compatible_(compatible),
|
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
|
|
|
|
|
WriteSdc::write()
|
|
|
|
|
{
|
|
|
|
|
openFile(filename_);
|
|
|
|
|
writeHeader();
|
|
|
|
|
writeTiming();
|
|
|
|
|
writeEnvironment();
|
|
|
|
|
writeDesignRules();
|
|
|
|
|
writeVariables();
|
|
|
|
|
closeFile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::openFile(const char *filename)
|
|
|
|
|
{
|
|
|
|
|
stream_ = fopen(filename, "w");
|
2019-03-13 01:25:53 +01:00
|
|
|
if (stream_ == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
throw FileNotWritable(filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::closeFile()
|
|
|
|
|
{
|
|
|
|
|
fclose(stream_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::flush()
|
|
|
|
|
{
|
|
|
|
|
fflush(stream_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeHeader() const
|
|
|
|
|
{
|
|
|
|
|
writeCommentSeparator();
|
|
|
|
|
fprintf(stream_, "# Created by %s\n", creator_);
|
|
|
|
|
if (!no_timestamp_) {
|
|
|
|
|
time_t now;
|
|
|
|
|
time(&now);
|
|
|
|
|
char *time_str = ctime(&now);
|
|
|
|
|
// Remove trailing \n.
|
|
|
|
|
time_str[strlen(time_str) - 1] = '\0';
|
|
|
|
|
fprintf(stream_, "# %s\n", time_str);
|
|
|
|
|
}
|
|
|
|
|
writeCommentSeparator();
|
|
|
|
|
|
|
|
|
|
fprintf(stream_, "current_design %s\n", sdc_network_->name(cell_));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
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.
|
2019-03-13 01:25:53 +01:00
|
|
|
for (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()) {
|
|
|
|
|
fprintf(stream_, "set_propagated_clock ");
|
|
|
|
|
writeGetClock(clk);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClock(Clock *clk) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "create_clock -name %s",
|
|
|
|
|
clk->name());
|
|
|
|
|
if (clk->addToPins())
|
|
|
|
|
fprintf(stream_, " -add");
|
|
|
|
|
fprintf(stream_, " -period ");
|
|
|
|
|
writeTime(clk->period());
|
|
|
|
|
fprintf(stream_, " -waveform ");
|
|
|
|
|
writeFloatSeq(clk->waveform(), scaleTime(1.0));
|
|
|
|
|
writeCmdComment(clk);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeClockPins(clk);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGeneratedClock(Clock *clk) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "create_generated_clock -name %s",
|
|
|
|
|
clk->name());
|
|
|
|
|
if (clk->addToPins())
|
|
|
|
|
fprintf(stream_, " -add");
|
|
|
|
|
fprintf(stream_, " -source ");
|
|
|
|
|
writeGetPin(clk->srcPin());
|
|
|
|
|
Clock *master = clk->masterClk();
|
|
|
|
|
if (master && !clk->masterClkInfered()) {
|
|
|
|
|
fprintf(stream_, " -master_clock ");
|
|
|
|
|
writeGetClock(master);
|
|
|
|
|
}
|
|
|
|
|
Pin *pll_out = clk->pllOut();
|
|
|
|
|
if (pll_out) {
|
|
|
|
|
fprintf(stream_, " -pll_out ");
|
|
|
|
|
writeGetPin(pll_out);
|
|
|
|
|
}
|
|
|
|
|
Pin *pll_fdbk = clk->pllFdbk();
|
|
|
|
|
if (pll_fdbk) {
|
|
|
|
|
fprintf(stream_, " -pll_feedback ");
|
|
|
|
|
writeGetPin(pll_fdbk);
|
|
|
|
|
}
|
|
|
|
|
if (clk->combinational())
|
|
|
|
|
fprintf(stream_, " -combinational");
|
|
|
|
|
int divide_by = clk->divideBy();
|
|
|
|
|
if (divide_by != 0)
|
|
|
|
|
fprintf(stream_, " -divide_by %d", divide_by);
|
|
|
|
|
int multiply_by = clk->multiplyBy();
|
|
|
|
|
if (multiply_by != 0)
|
|
|
|
|
fprintf(stream_, " -multiply_by %d", multiply_by);
|
|
|
|
|
float duty_cycle = clk->dutyCycle();
|
|
|
|
|
if (duty_cycle != 0.0) {
|
|
|
|
|
fprintf(stream_, " -duty_cycle ");
|
|
|
|
|
writeFloat(duty_cycle);
|
|
|
|
|
}
|
|
|
|
|
if (clk->invert())
|
|
|
|
|
fprintf(stream_, " -invert");
|
|
|
|
|
IntSeq *edges = clk->edges();
|
|
|
|
|
if (edges && !edges->empty()) {
|
|
|
|
|
fprintf(stream_, " -edges ");
|
|
|
|
|
writeIntSeq(edges);
|
|
|
|
|
FloatSeq *edge_shifts = clk->edgeShifts();
|
|
|
|
|
if (edge_shifts && !edge_shifts->empty()) {
|
|
|
|
|
fprintf(stream_, " -edge_shift ");
|
|
|
|
|
writeFloatSeq(edge_shifts, scaleTime(1.0));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
writeCmdComment(clk);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeClockPins(clk);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockPins(Clock *clk) const
|
|
|
|
|
{
|
|
|
|
|
// Sort pins.
|
|
|
|
|
PinSeq pins;
|
|
|
|
|
ClockPinIterator pin_iter(clk);
|
|
|
|
|
while (pin_iter.hasNext())
|
|
|
|
|
pins.push_back(pin_iter.next());
|
|
|
|
|
|
|
|
|
|
if (!pins.empty()) {
|
|
|
|
|
sort(pins, PinPathNameLess(sdc_network_));
|
|
|
|
|
if (pins.size() > 1)
|
|
|
|
|
fprintf(stream_, "\\\n ");
|
|
|
|
|
writeGetPins(&pins);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockSlews(Clock *clk) const
|
|
|
|
|
{
|
|
|
|
|
WriteGetClock write_clk(clk, this);
|
|
|
|
|
RiseFallMinMax *slews = clk->slews();
|
|
|
|
|
if (slews->hasValue())
|
|
|
|
|
writeRiseFallMinMaxTimeCmd("set_clock_transition", slews, write_clk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockUncertainty(Clock *clk) const
|
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
WriteSdc::writeClockUncertainty(Clock *clk,
|
|
|
|
|
const char *setup_hold,
|
|
|
|
|
float value) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "set_clock_uncertainty %s", setup_hold);
|
|
|
|
|
writeTime(value);
|
|
|
|
|
fprintf(stream_, " %s\n", clk->name());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "set_clock_uncertainty %s", setup_hold);
|
|
|
|
|
writeTime(value);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeGetPin(pin);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
WriteGetPinAndClkKey write_pin(pin, clk, this);
|
|
|
|
|
writeRiseFallMinMaxTimeCmd("set_clock_latency", latency->delays(),
|
|
|
|
|
write_pin);
|
|
|
|
|
}
|
|
|
|
|
else if (pin) {
|
|
|
|
|
WriteGetPin write_pin(pin, this);
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
ClockInsertions::Iterator insert_iter(sdc_->clockInsertions());
|
|
|
|
|
while (insert_iter.hasNext()) {
|
|
|
|
|
ClockInsertion *insert = insert_iter.next();
|
|
|
|
|
const Pin *pin = insert->pin();
|
|
|
|
|
const Clock *clk = insert->clock();
|
|
|
|
|
if (pin && clk) {
|
|
|
|
|
WriteGetPinAndClkKey write_pin_clk(pin, clk, this);
|
|
|
|
|
writeClockInsertion(insert, write_pin_clk);
|
|
|
|
|
}
|
|
|
|
|
else if (pin) {
|
|
|
|
|
WriteGetPin write_pin(pin, this);
|
|
|
|
|
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();
|
|
|
|
|
fprintf(stream_, "set_propagated_clock ");
|
|
|
|
|
writeGetPin(pin);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 =
|
|
|
|
|
uncertainty->uncertainties(TransRiseFall::rise());
|
|
|
|
|
const RiseFallMinMax *src_fall =
|
|
|
|
|
uncertainty->uncertainties(TransRiseFall::fall());
|
|
|
|
|
float value;
|
|
|
|
|
if (src_rise->equal(src_fall)
|
|
|
|
|
&& src_rise->isOneValue(value)) {
|
|
|
|
|
fprintf(stream_, "set_clock_uncertainty -from ");
|
|
|
|
|
writeGetClock(src_clk);
|
|
|
|
|
fprintf(stream_, " -to ");
|
|
|
|
|
writeGetClock(tgt_clk);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeTime(value);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
else {
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto src_tr : TransRiseFall::range()) {
|
|
|
|
|
for (auto tgt_tr : TransRiseFall::range()) {
|
|
|
|
|
for (auto setup_hold : SetupHold::range()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
float value;
|
|
|
|
|
bool exists;
|
|
|
|
|
sdc_->clockUncertainty(src_clk, src_tr, tgt_clk, tgt_tr,
|
|
|
|
|
setup_hold, value, exists);
|
|
|
|
|
if (exists) {
|
|
|
|
|
fprintf(stream_, "set_clock_uncertainty -%s_from ",
|
|
|
|
|
src_tr == TransRiseFall::rise() ? "rise" : "fall");
|
|
|
|
|
writeGetClock(uncertainty->src());
|
|
|
|
|
fprintf(stream_, " -%s_to ",
|
|
|
|
|
tgt_tr == TransRiseFall::rise() ? "rise" : "fall");
|
|
|
|
|
writeGetClock(uncertainty->target());
|
|
|
|
|
fprintf(stream_, " %s ",
|
|
|
|
|
setupHoldFlag(setup_hold));
|
|
|
|
|
writeTime(value);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeInputDelays() const
|
|
|
|
|
{
|
|
|
|
|
// Sort arrivals by pin and clock name.
|
|
|
|
|
PortDelaySeq delays;
|
2019-03-13 01:25:53 +01:00
|
|
|
InputDelayIterator input_iter(sdc_);
|
|
|
|
|
while (input_iter.hasNext()) {
|
|
|
|
|
InputDelay *input_delay = input_iter.next();
|
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();
|
|
|
|
|
writePortDelay(input_delay, "set_input_delay");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeOutputDelays() const
|
|
|
|
|
{
|
|
|
|
|
// Sort departures by pin and clock name.
|
|
|
|
|
PortDelaySeq delays;
|
2019-03-13 01:25:53 +01:00
|
|
|
OutputDelayIterator output_iter(sdc_);
|
|
|
|
|
while (output_iter.hasNext()) {
|
|
|
|
|
OutputDelay *output_delay = output_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
delays.push_back(output_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 *output_delay = delay_iter.next();
|
|
|
|
|
writePortDelay(output_delay, "set_output_delay");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writePortDelay(PortDelay *port_delay,
|
|
|
|
|
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;
|
|
|
|
|
delays->value(TransRiseFall::rise(), MinMax::min(),
|
|
|
|
|
rise_min, rise_min_exists);
|
|
|
|
|
delays->value(TransRiseFall::rise(), MinMax::max(),
|
|
|
|
|
rise_max, rise_max_exists);
|
|
|
|
|
delays->value(TransRiseFall::fall(), MinMax::min(),
|
|
|
|
|
fall_min, fall_min_exists);
|
|
|
|
|
delays->value(TransRiseFall::fall(), MinMax::max(),
|
|
|
|
|
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)
|
|
|
|
|
writePortDelay(port_delay, rise_min,
|
|
|
|
|
TransRiseFallBoth::riseFall(), MinMaxAll::all(), sdc_cmd);
|
|
|
|
|
else if (rise_min_exists
|
|
|
|
|
&& rise_max_exists
|
|
|
|
|
&& rise_max == rise_min
|
|
|
|
|
&& fall_min_exists
|
|
|
|
|
&& fall_max_exists
|
|
|
|
|
&& fall_min == fall_max) {
|
|
|
|
|
writePortDelay(port_delay, rise_min,
|
|
|
|
|
TransRiseFallBoth::rise(), MinMaxAll::all(), sdc_cmd);
|
|
|
|
|
writePortDelay(port_delay, fall_min,
|
|
|
|
|
TransRiseFallBoth::fall(), MinMaxAll::all(), sdc_cmd);
|
|
|
|
|
}
|
|
|
|
|
else if (rise_min_exists
|
|
|
|
|
&& fall_min_exists
|
|
|
|
|
&& rise_min == fall_min
|
|
|
|
|
&& rise_max_exists
|
|
|
|
|
&& fall_max_exists
|
|
|
|
|
&& rise_max == fall_max) {
|
|
|
|
|
writePortDelay(port_delay, rise_min,
|
|
|
|
|
TransRiseFallBoth::riseFall(), MinMaxAll::min(), sdc_cmd);
|
|
|
|
|
writePortDelay(port_delay, rise_max,
|
|
|
|
|
TransRiseFallBoth::riseFall(), MinMaxAll::max(), sdc_cmd);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (rise_min_exists)
|
|
|
|
|
writePortDelay(port_delay, rise_min,
|
|
|
|
|
TransRiseFallBoth::rise(), MinMaxAll::min(), sdc_cmd);
|
|
|
|
|
if (rise_max_exists)
|
|
|
|
|
writePortDelay(port_delay, rise_max,
|
|
|
|
|
TransRiseFallBoth::rise(), MinMaxAll::max(), sdc_cmd);
|
|
|
|
|
if (fall_min_exists)
|
|
|
|
|
writePortDelay(port_delay, fall_min,
|
|
|
|
|
TransRiseFallBoth::fall(), MinMaxAll::min(), sdc_cmd);
|
|
|
|
|
if (fall_max_exists)
|
|
|
|
|
writePortDelay(port_delay, fall_max,
|
|
|
|
|
TransRiseFallBoth::fall(), MinMaxAll::max(), sdc_cmd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writePortDelay(PortDelay *port_delay,
|
|
|
|
|
float delay,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
const char *sdc_cmd) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "%s ", sdc_cmd);
|
|
|
|
|
writeTime(delay);
|
|
|
|
|
ClockEdge *clk_edge = port_delay->clkEdge();
|
|
|
|
|
if (clk_edge) {
|
|
|
|
|
writeClockKey(clk_edge->clock());
|
|
|
|
|
if (clk_edge->transition() == TransRiseFall::fall())
|
|
|
|
|
fprintf(stream_, " -clock_fall");
|
|
|
|
|
}
|
|
|
|
|
fprintf(stream_, "%s%s -add_delay ",
|
|
|
|
|
transRiseFallFlag(tr),
|
|
|
|
|
minMaxFlag(min_max));
|
|
|
|
|
Pin *ref_pin = port_delay->refPin();
|
|
|
|
|
if (ref_pin) {
|
|
|
|
|
fprintf(stream_, "-reference_pin ");
|
|
|
|
|
writeGetPin(ref_pin);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
}
|
|
|
|
|
Pin *pin = port_delay->pin();
|
|
|
|
|
if (sdc_network_->instance(pin) == instance_) {
|
|
|
|
|
Port *port = sdc_network_->port(pin);
|
|
|
|
|
writeGetPort(port);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
writeGetPin(pin);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
for (auto iter : sdc_->clk_sense_map_)
|
|
|
|
|
pin_clks.push_back(iter.first);
|
|
|
|
|
|
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";
|
2019-06-24 04:52:29 +02:00
|
|
|
fprintf(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) {
|
|
|
|
|
fprintf(stream_, "-clock ");
|
|
|
|
|
writeGetClock(clk);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
writeGetPin(pin_clk.first);
|
2018-09-28 17:54:21 +02:00
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
ClockSet *clk_set1 = clk_group1->clks();
|
|
|
|
|
ClockSet *clk_set2 = clk_group2->clks();
|
|
|
|
|
size_t size1 = clk_set1->size();
|
|
|
|
|
size_t size2 = clk_set2->size();
|
|
|
|
|
if (size1 < size2)
|
|
|
|
|
return true;
|
|
|
|
|
else if (size1 > size2)
|
|
|
|
|
return false;
|
|
|
|
|
else {
|
|
|
|
|
ClockSeq clks1, clks2;
|
|
|
|
|
ClockSet::Iterator clk_iter1(clk_set1);
|
|
|
|
|
while (clk_iter1.hasNext())
|
|
|
|
|
clks1.push_back(clk_iter1.next());
|
|
|
|
|
sort(clks1, ClockNameLess());
|
|
|
|
|
|
|
|
|
|
ClockSet::Iterator clk_iter2(clk_set2);
|
|
|
|
|
while (clk_iter2.hasNext())
|
|
|
|
|
clks2.push_back(clk_iter2.next());
|
|
|
|
|
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
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
ClockGroupIterator groups_iter(sdc_);
|
|
|
|
|
while (groups_iter.hasNext()) {
|
|
|
|
|
ClockGroups *clk_groups = groups_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
writeClockGroups(clk_groups);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockGroups(ClockGroups *clk_groups) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "set_clock_groups -name %s ", clk_groups->name());
|
|
|
|
|
if (clk_groups->logicallyExclusive())
|
|
|
|
|
fprintf(stream_, "-logically_exclusive \\\n");
|
|
|
|
|
else if (clk_groups->physicallyExclusive())
|
|
|
|
|
fprintf(stream_, "-physically_exclusive \\\n");
|
|
|
|
|
else if (clk_groups->asynchronous())
|
|
|
|
|
fprintf(stream_, "-asynchronous \\\n");
|
|
|
|
|
if (clk_groups->allowPaths())
|
|
|
|
|
fprintf(stream_, "-allow_paths \\\n");
|
|
|
|
|
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)
|
|
|
|
|
fprintf(stream_, "\\\n");
|
|
|
|
|
fprintf(stream_, " -group ");
|
|
|
|
|
writeGetClocks(clk_group->clks());
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
writeCmdComment(clk_groups);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisables() const
|
|
|
|
|
{
|
|
|
|
|
writeDisabledCells();
|
|
|
|
|
writeDisabledPorts();
|
|
|
|
|
writeDisabledLibPorts();
|
|
|
|
|
writeDisabledInstances();
|
|
|
|
|
writeDisabledPins();
|
|
|
|
|
writeDisabledEdges();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledCells() const
|
|
|
|
|
{
|
|
|
|
|
DisabledCellPortsSeq disables;
|
|
|
|
|
sortDisabledCellPortsMap(sdc_->disabledCellPorts(), disables);
|
|
|
|
|
DisabledCellPortsSeq::Iterator disabled_iter(disables);
|
|
|
|
|
while (disabled_iter.hasNext()) {
|
|
|
|
|
DisabledCellPorts *disable = disabled_iter.next();
|
|
|
|
|
LibertyCell *cell = disable->cell();
|
|
|
|
|
if (disable->all()) {
|
|
|
|
|
fprintf(stream_, "set_disable_timing ");
|
|
|
|
|
writeGetLibCell(cell);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
if (disable->fromTo()) {
|
|
|
|
|
LibertyPortPairSeq pairs;
|
|
|
|
|
sortLibertyPortPairSet(disable->fromTo(), pairs);
|
|
|
|
|
LibertyPortPairSeq::Iterator pair_iter(pairs);
|
|
|
|
|
while (pair_iter.hasNext()) {
|
|
|
|
|
LibertyPortPair *from_to = pair_iter.next();
|
2018-12-05 23:18:41 +01:00
|
|
|
const LibertyPort *from = from_to->first;
|
|
|
|
|
const LibertyPort *to = from_to->second;
|
2018-09-28 17:54:21 +02:00
|
|
|
fprintf(stream_, "set_disable_timing -from {%s} -to {%s} ",
|
|
|
|
|
from->name(),
|
|
|
|
|
to->name());
|
|
|
|
|
writeGetLibCell(cell);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (disable->from()) {
|
|
|
|
|
LibertyPortSeq from;
|
|
|
|
|
sortLibertyPortSet(disable->from(), from);
|
|
|
|
|
LibertyPortSeq::Iterator from_iter(from);
|
|
|
|
|
while (from_iter.hasNext()) {
|
|
|
|
|
LibertyPort *from_port = from_iter.next();
|
|
|
|
|
fprintf(stream_, "set_disable_timing -from {%s} ",
|
|
|
|
|
from_port->name());
|
|
|
|
|
writeGetLibCell(cell);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (disable->to()) {
|
|
|
|
|
LibertyPortSeq to;
|
|
|
|
|
sortLibertyPortSet(disable->to(), to);
|
|
|
|
|
LibertyPortSeq::Iterator to_iter(to);
|
|
|
|
|
while (to_iter.hasNext()) {
|
|
|
|
|
LibertyPort *to_port = to_iter.next();
|
|
|
|
|
fprintf(stream_, "set_disable_timing -to {%s} ",
|
|
|
|
|
to_port->name());
|
|
|
|
|
writeGetLibCell(cell);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (disable->timingArcSets()) {
|
|
|
|
|
// The only syntax to disable timing arc sets disables all of the
|
|
|
|
|
// cell's timing arc sets.
|
|
|
|
|
fprintf(stream_, "set_disable_timing ");
|
|
|
|
|
writeGetTimingArcsOfOjbects(cell);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledPorts() const
|
|
|
|
|
{
|
|
|
|
|
PortSeq ports;
|
|
|
|
|
sortPortSet(sdc_->disabledPorts(), sdc_network_, ports);
|
|
|
|
|
PortSeq::Iterator port_iter(ports);
|
|
|
|
|
while (port_iter.hasNext()) {
|
|
|
|
|
Port *port = port_iter.next();
|
|
|
|
|
fprintf(stream_, "set_disable_timing ");
|
|
|
|
|
writeGetPort(port);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledLibPorts() const
|
|
|
|
|
{
|
|
|
|
|
LibertyPortSeq ports;
|
|
|
|
|
sortLibertyPortSet(sdc_->disabledLibPorts(), ports);
|
|
|
|
|
LibertyPortSeq::Iterator port_iter(ports);
|
|
|
|
|
while (port_iter.hasNext()) {
|
|
|
|
|
LibertyPort *port = port_iter.next();
|
|
|
|
|
fprintf(stream_, "set_disable_timing ");
|
|
|
|
|
writeGetLibPin(port);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledInstances() const
|
|
|
|
|
{
|
|
|
|
|
DisabledInstancePortsSeq disables;
|
|
|
|
|
sortDisabledInstancePortsMap(sdc_->disabledInstancePorts(),
|
|
|
|
|
sdc_network_, disables);
|
|
|
|
|
DisabledInstancePortsSeq::Iterator disabled_iter(disables);
|
|
|
|
|
while (disabled_iter.hasNext()) {
|
|
|
|
|
DisabledInstancePorts *disable = disabled_iter.next();
|
|
|
|
|
Instance *inst = disable->instance();
|
|
|
|
|
if (disable->all()) {
|
|
|
|
|
fprintf(stream_, "set_disable_timing ");
|
|
|
|
|
writeGetInstance(inst);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
else if (disable->fromTo()) {
|
|
|
|
|
LibertyPortPairSeq pairs;
|
|
|
|
|
sortLibertyPortPairSet(disable->fromTo(), pairs);
|
|
|
|
|
LibertyPortPairSeq::Iterator pair_iter(pairs);
|
|
|
|
|
while (pair_iter.hasNext()) {
|
|
|
|
|
LibertyPortPair *from_to = pair_iter.next();
|
2018-12-05 23:18:41 +01:00
|
|
|
const LibertyPort *from_port = from_to->first;
|
|
|
|
|
const LibertyPort *to_port = from_to->second;
|
2018-09-28 17:54:21 +02:00
|
|
|
fprintf(stream_, "set_disable_timing -from {%s} -to {%s} ",
|
|
|
|
|
from_port->name(),
|
|
|
|
|
to_port->name());
|
|
|
|
|
writeGetInstance(inst);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (disable->from()) {
|
|
|
|
|
LibertyPortSeq from;
|
|
|
|
|
sortLibertyPortSet(disable->from(), from);
|
|
|
|
|
LibertyPortSeq::Iterator from_iter(from);
|
|
|
|
|
while (from_iter.hasNext()) {
|
|
|
|
|
LibertyPort *from_port = from_iter.next();
|
|
|
|
|
fprintf(stream_, "set_disable_timing -from {%s} ",
|
|
|
|
|
from_port->name());
|
|
|
|
|
writeGetInstance(inst);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (disable->to()) {
|
|
|
|
|
LibertyPortSeq to;
|
|
|
|
|
sortLibertyPortSet(disable->to(), to);
|
|
|
|
|
LibertyPortSeq::Iterator to_iter(to);
|
|
|
|
|
while (to_iter.hasNext()) {
|
|
|
|
|
LibertyPort *to_port = to_iter.next();
|
|
|
|
|
fprintf(stream_, "set_disable_timing -to {%s} ",
|
|
|
|
|
to_port->name());
|
|
|
|
|
writeGetInstance(inst);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledPins() const
|
|
|
|
|
{
|
|
|
|
|
PinSeq pins;
|
|
|
|
|
sortPinSet(sdc_->disabledPins(), sdc_network_, pins);
|
|
|
|
|
PinSeq::Iterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
fprintf(stream_, "set_disable_timing ");
|
|
|
|
|
writeGetPin(pin);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledEdges() const
|
|
|
|
|
{
|
|
|
|
|
EdgeSeq edges;
|
|
|
|
|
EdgeSet::Iterator edge_iter(sdc_->disabledEdges());
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
edges.push_back(edge);
|
|
|
|
|
}
|
|
|
|
|
sortEdges(&edges, sdc_network_, graph_);
|
|
|
|
|
EdgeSeq::Iterator edge_iter2(edges);
|
|
|
|
|
while (edge_iter2.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter2.next();
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
EdgeSet::Iterator match_iter(matches);
|
|
|
|
|
while (match_iter.hasNext()) {
|
|
|
|
|
Edge *match = match_iter.next();
|
|
|
|
|
if (match != edge
|
|
|
|
|
&& match->sense() == edge->sense())
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledEdge(Edge *edge) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "set_disable_timing ");
|
|
|
|
|
writeGetTimingArcs(edge);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDisabledEdgeSense(Edge *edge) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "set_disable_timing ");
|
|
|
|
|
const char *sense = timingSenseString(edge->sense());
|
2019-01-17 00:37:31 +01:00
|
|
|
string filter;
|
|
|
|
|
stringPrint(filter, "sense == %s", sense);
|
|
|
|
|
writeGetTimingArcs(edge, filter.c_str());
|
2018-09-28 17:54:21 +02:00
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptions() const
|
|
|
|
|
{
|
|
|
|
|
ExceptionPathSeq exceptions;
|
|
|
|
|
sortExceptions(sdc_->exceptions(), exceptions, sdc_network_);
|
|
|
|
|
ExceptionPathSeq::Iterator except_iter(exceptions);
|
|
|
|
|
while (except_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *exception = except_iter.next();
|
|
|
|
|
if (!exception->isFilter()
|
|
|
|
|
&& !exception->isLoop())
|
|
|
|
|
writeException(exception);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeException(ExceptionPath *exception) const
|
|
|
|
|
{
|
|
|
|
|
writeExceptionCmd(exception);
|
|
|
|
|
if (exception->from())
|
|
|
|
|
writeExceptionFrom(exception->from());
|
|
|
|
|
if (exception->thrus()) {
|
|
|
|
|
ExceptionThruSeq::Iterator thru_iter(exception->thrus());
|
|
|
|
|
while (thru_iter.hasNext()) {
|
|
|
|
|
ExceptionThru *thru = thru_iter.next();
|
|
|
|
|
writeExceptionThru(thru);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (exception->to())
|
|
|
|
|
writeExceptionTo(exception->to());
|
|
|
|
|
writeExceptionValue(exception);
|
|
|
|
|
writeCmdComment(exception);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptionCmd(ExceptionPath *exception) const
|
|
|
|
|
{
|
|
|
|
|
if (exception->isFalse()) {
|
|
|
|
|
fprintf(stream_, "set_false_path");
|
|
|
|
|
writeSetupHoldFlag(exception->minMax());
|
|
|
|
|
}
|
|
|
|
|
else if (exception->isMultiCycle()) {
|
|
|
|
|
fprintf(stream_, "set_multicycle_path");
|
|
|
|
|
const MinMaxAll *min_max = exception->minMax();
|
|
|
|
|
writeSetupHoldFlag(min_max);
|
|
|
|
|
if (min_max == MinMaxAll::min()) {
|
|
|
|
|
// For hold MCPs default is -start.
|
|
|
|
|
if (exception->useEndClk())
|
|
|
|
|
fprintf(stream_, " -end");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// For setup MCPs default is -end.
|
|
|
|
|
if (!exception->useEndClk())
|
|
|
|
|
fprintf(stream_, " -start");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (exception->isPathDelay()) {
|
|
|
|
|
if (exception->minMax() == MinMaxAll::max())
|
|
|
|
|
fprintf(stream_, "set_max_delay");
|
|
|
|
|
else
|
|
|
|
|
fprintf(stream_, "set_min_delay");
|
|
|
|
|
if (exception->ignoreClkLatency())
|
|
|
|
|
fprintf(stream_, " -ignore_clock_latency");
|
|
|
|
|
}
|
|
|
|
|
else if (exception->isGroupPath()) {
|
|
|
|
|
if (exception->isDefault())
|
|
|
|
|
fprintf(stream_, "group_path -default");
|
|
|
|
|
else
|
|
|
|
|
fprintf(stream_, "group_path -name %s", exception->name());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
internalError("unknown exception type");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptionValue(ExceptionPath *exception) const
|
|
|
|
|
{
|
|
|
|
|
if (exception->isMultiCycle())
|
|
|
|
|
fprintf(stream_, " %d",
|
|
|
|
|
exception->pathMultiplier());
|
|
|
|
|
else if (exception->isPathDelay()) {
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeTime(exception->delay());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptionFrom(ExceptionFrom *from) const
|
|
|
|
|
{
|
|
|
|
|
writeExceptionFromTo(from, "from");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptionTo(ExceptionTo *to) const
|
|
|
|
|
{
|
|
|
|
|
const TransRiseFallBoth *end_tr = to->endTransition();
|
|
|
|
|
if (end_tr != TransRiseFallBoth::riseFall())
|
|
|
|
|
fprintf(stream_, "%s ", transRiseFallFlag(end_tr));
|
|
|
|
|
if (to->hasObjects())
|
|
|
|
|
writeExceptionFromTo(to, "to");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptionFromTo(ExceptionFromTo *from_to,
|
|
|
|
|
const char *from_to_key) const
|
|
|
|
|
{
|
|
|
|
|
const TransRiseFallBoth *tr = from_to->transition();
|
|
|
|
|
const char *tr_prefix = "-";
|
|
|
|
|
if (tr == TransRiseFallBoth::rise())
|
|
|
|
|
tr_prefix = "-rise_";
|
|
|
|
|
else if (tr == TransRiseFallBoth::fall())
|
|
|
|
|
tr_prefix = "-fall_";
|
|
|
|
|
fprintf(stream_, "\\\n %s%s ", tr_prefix, from_to_key);
|
|
|
|
|
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)
|
|
|
|
|
fprintf(stream_, "[list ");
|
|
|
|
|
bool first = true;
|
|
|
|
|
if (from_to->pins()) {
|
|
|
|
|
PinSeq pins;
|
|
|
|
|
sortPinSet(from_to->pins(), sdc_network_, pins);
|
|
|
|
|
PinSeq::Iterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
if (multi_objs && !first)
|
|
|
|
|
fprintf(stream_, "\\\n ");
|
|
|
|
|
writeGetPin(pin);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (from_to->clks())
|
|
|
|
|
writeGetClocks(from_to->clks(), multi_objs, first);
|
|
|
|
|
if (from_to->instances()) {
|
|
|
|
|
InstanceSeq insts;
|
|
|
|
|
sortInstanceSet(from_to->instances(), sdc_network_, insts);
|
|
|
|
|
InstanceSeq::Iterator inst_iter(insts);
|
|
|
|
|
while (inst_iter.hasNext()) {
|
|
|
|
|
Instance *inst = inst_iter.next();
|
|
|
|
|
if (multi_objs && !first)
|
|
|
|
|
fprintf(stream_, "\\\n ");
|
|
|
|
|
writeGetInstance(inst);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (multi_objs)
|
|
|
|
|
fprintf(stream_, "]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeExceptionThru(ExceptionThru *thru) const
|
|
|
|
|
{
|
|
|
|
|
const TransRiseFallBoth *tr = thru->transition();
|
|
|
|
|
const char *tr_prefix = "-";
|
|
|
|
|
if (tr == TransRiseFallBoth::rise())
|
|
|
|
|
tr_prefix = "-rise_";
|
|
|
|
|
else if (tr == TransRiseFallBoth::fall())
|
|
|
|
|
tr_prefix = "-fall_";
|
|
|
|
|
fprintf(stream_, "\\\n %sthrough ", tr_prefix);
|
|
|
|
|
bool multi_objs =
|
|
|
|
|
((thru->pins() ? thru->pins()->size() : 0)
|
|
|
|
|
+ (thru->nets() ? thru->nets()->size() : 0)
|
|
|
|
|
+ (thru->instances() ? thru->instances()->size() : 0)) > 1;
|
|
|
|
|
if (multi_objs)
|
|
|
|
|
fprintf(stream_, "[list ");
|
|
|
|
|
bool first = true;
|
|
|
|
|
if (thru->pins()) {
|
|
|
|
|
PinSeq pins;
|
|
|
|
|
sortPinSet(thru->pins(), sdc_network_, pins);
|
|
|
|
|
PinSeq::Iterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
if (multi_objs && !first)
|
|
|
|
|
fprintf(stream_, "\\\n ");
|
|
|
|
|
writeGetPin(pin);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (thru->nets()) {
|
|
|
|
|
NetSeq nets;
|
|
|
|
|
sortNetSet(thru->nets(), sdc_network_, nets);
|
|
|
|
|
NetSeq::Iterator net_iter(nets);
|
|
|
|
|
while (net_iter.hasNext()) {
|
|
|
|
|
Net *net = net_iter.next();
|
|
|
|
|
if (multi_objs && !first)
|
|
|
|
|
fprintf(stream_, "\\\n ");
|
|
|
|
|
writeGetNet(net);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (thru->instances()) {
|
|
|
|
|
InstanceSeq insts;
|
|
|
|
|
sortInstanceSet(thru->instances(), sdc_network_, insts);
|
|
|
|
|
InstanceSeq::Iterator inst_iter(insts);
|
|
|
|
|
while (inst_iter.hasNext()) {
|
|
|
|
|
Instance *inst = inst_iter.next();
|
|
|
|
|
if (multi_objs && !first)
|
|
|
|
|
fprintf(stream_, "\\\n ");
|
|
|
|
|
writeGetInstance(inst);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (multi_objs)
|
|
|
|
|
fprintf(stream_, "]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDataChecks() const
|
|
|
|
|
{
|
|
|
|
|
Vector<DataCheck*> checks;
|
|
|
|
|
DataChecksMap::Iterator checks_iter(sdc_->data_checks_to_map_);
|
|
|
|
|
while (checks_iter.hasNext()) {
|
|
|
|
|
DataCheckSet *checks1 = checks_iter.next();
|
|
|
|
|
DataCheckSet::Iterator check_iter(checks1);
|
|
|
|
|
while (check_iter.hasNext()) {
|
|
|
|
|
DataCheck *check = check_iter.next();
|
|
|
|
|
checks.push_back(check);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sort(checks, DataCheckLess(sdc_network_));
|
|
|
|
|
Vector<DataCheck*>::Iterator check_iter(checks);
|
|
|
|
|
while (check_iter.hasNext()) {
|
|
|
|
|
DataCheck *check = check_iter.next();
|
|
|
|
|
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)
|
|
|
|
|
writeDataCheck(check, TransRiseFallBoth::riseFall(),
|
|
|
|
|
TransRiseFallBoth::riseFall(), setup_hold, margin);
|
|
|
|
|
else {
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto from_tr : TransRiseFall::range()) {
|
|
|
|
|
for (auto to_tr : TransRiseFall::range()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
float margin;
|
|
|
|
|
bool margin_exists;
|
|
|
|
|
check->margin(from_tr, to_tr, setup_hold, margin, margin_exists);
|
|
|
|
|
if (margin_exists) {
|
|
|
|
|
writeDataCheck(check, from_tr->asRiseFallBoth(),
|
|
|
|
|
to_tr->asRiseFallBoth(), setup_hold, margin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDataCheck(DataCheck *check,
|
|
|
|
|
TransRiseFallBoth *from_tr,
|
|
|
|
|
TransRiseFallBoth *to_tr,
|
|
|
|
|
SetupHold *setup_hold,
|
|
|
|
|
float margin) const
|
|
|
|
|
{
|
|
|
|
|
const char *from_key = "-from";
|
|
|
|
|
if (from_tr == TransRiseFallBoth::rise())
|
|
|
|
|
from_key = "-rise_from";
|
|
|
|
|
else if (from_tr == TransRiseFallBoth::fall())
|
|
|
|
|
from_key = "-fall_from";
|
|
|
|
|
fprintf(stream_, "set_data_check %s ", from_key);
|
|
|
|
|
writeGetPin(check->from());
|
|
|
|
|
const char *to_key = "-to";
|
|
|
|
|
if (to_tr == TransRiseFallBoth::rise())
|
|
|
|
|
to_key = "-rise_to";
|
|
|
|
|
else if (to_tr == TransRiseFallBoth::fall())
|
|
|
|
|
to_key = "-fall_to";
|
|
|
|
|
fprintf(stream_, " %s ", to_key);
|
|
|
|
|
writeGetPin(check->to());
|
|
|
|
|
fprintf(stream_, "%s ",
|
|
|
|
|
setupHoldFlag(setup_hold));
|
|
|
|
|
writeTime(margin);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeEnvironment() const
|
|
|
|
|
{
|
|
|
|
|
writeCommentSection("Environment");
|
|
|
|
|
writeOperatingConditions();
|
|
|
|
|
writeWireload();
|
|
|
|
|
writePinLoads();
|
|
|
|
|
writeDriveResistances();
|
|
|
|
|
writeDrivingCells();
|
|
|
|
|
writeInputTransitions();
|
|
|
|
|
writeNetResistances();
|
|
|
|
|
writeConstants();
|
|
|
|
|
writeCaseAnalysis();
|
|
|
|
|
writeDeratings();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeOperatingConditions() const
|
|
|
|
|
{
|
|
|
|
|
OperatingConditions *cond = sdc_->operatingConditions(MinMax::max());
|
|
|
|
|
if (cond)
|
|
|
|
|
fprintf(stream_, "set_operating_conditions %s\n", cond->name());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeWireload() const
|
|
|
|
|
{
|
|
|
|
|
WireloadMode wireload_mode = sdc_->wireloadMode();
|
2019-03-13 01:25:53 +01:00
|
|
|
if (wireload_mode != WireloadMode::unknown)
|
2018-09-28 17:54:21 +02:00
|
|
|
fprintf(stream_, "set_wire_load_mode \"%s\"\n",
|
|
|
|
|
wireloadModeString(wireload_mode));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writePinLoads() const
|
|
|
|
|
{
|
|
|
|
|
CellPortBitIterator *port_iter = sdc_network_->portBitIterator(cell_);
|
|
|
|
|
while (port_iter->hasNext()) {
|
|
|
|
|
Port *port = port_iter->next();
|
|
|
|
|
writePortLoads(port);
|
|
|
|
|
}
|
|
|
|
|
delete port_iter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writePortLoads(Port *port) const
|
|
|
|
|
{
|
|
|
|
|
PortExtCap *ext_cap = sdc_->portExtCap(port);
|
|
|
|
|
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) {
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto tr : TransRiseFall::range()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (drive->driveResistanceMinMaxEqual(tr)) {
|
|
|
|
|
float res;
|
|
|
|
|
bool exists;
|
|
|
|
|
drive->driveResistance(tr, MinMax::max(), res, exists);
|
|
|
|
|
fprintf(stream_, "set_drive %s ",
|
|
|
|
|
transRiseFallFlag(tr));
|
|
|
|
|
writeResistance(res);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeGetPort(port);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
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;
|
2019-07-18 15:19:00 +02:00
|
|
|
drive->driveResistance(tr, min_max, res, exists);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (exists) {
|
|
|
|
|
fprintf(stream_, "set_drive %s %s ",
|
|
|
|
|
transRiseFallFlag(tr),
|
2019-07-18 15:19:00 +02:00
|
|
|
minMaxFlag(min_max));
|
2018-09-28 17:54:21 +02:00
|
|
|
writeResistance(res);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeGetPort(port);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
|
InputDriveCell *drive_rise_min = drive->driveCell(TransRiseFall::rise(),
|
|
|
|
|
MinMax::min());
|
|
|
|
|
InputDriveCell *drive_rise_max = drive->driveCell(TransRiseFall::rise(),
|
|
|
|
|
MinMax::max());
|
|
|
|
|
InputDriveCell *drive_fall_min = drive->driveCell(TransRiseFall::fall(),
|
|
|
|
|
MinMax::min());
|
|
|
|
|
InputDriveCell *drive_fall_max = drive->driveCell(TransRiseFall::fall(),
|
|
|
|
|
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-03-13 01:25:53 +01:00
|
|
|
writeDrivingCell(port, drive_rise_min, TransRiseFall::rise(), nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
else {
|
|
|
|
|
if (drive_rise_min)
|
|
|
|
|
writeDrivingCell(port, drive_rise_min, TransRiseFall::rise(),
|
|
|
|
|
MinMax::min());
|
|
|
|
|
if (drive_rise_max)
|
|
|
|
|
writeDrivingCell(port, drive_rise_max, TransRiseFall::rise(),
|
|
|
|
|
MinMax::max());
|
|
|
|
|
}
|
|
|
|
|
if (drive_fall_min
|
|
|
|
|
&& drive_fall_max
|
|
|
|
|
&& drive_fall_min->equal(drive_fall_max))
|
2019-03-13 01:25:53 +01:00
|
|
|
writeDrivingCell(port, drive_fall_min, TransRiseFall::fall(), nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
else {
|
|
|
|
|
if (drive_fall_min)
|
|
|
|
|
writeDrivingCell(port, drive_fall_min, TransRiseFall::fall(),
|
|
|
|
|
MinMax::min());
|
|
|
|
|
if (drive_fall_max)
|
|
|
|
|
writeDrivingCell(port, drive_fall_max, TransRiseFall::fall(),
|
|
|
|
|
MinMax::max());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete port_iter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDrivingCell(Port *port,
|
|
|
|
|
InputDriveCell *drive_cell,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const MinMax *min_max) const
|
|
|
|
|
{
|
|
|
|
|
LibertyCell *cell = drive_cell->cell();
|
|
|
|
|
LibertyPort *from_port = drive_cell->fromPort();
|
|
|
|
|
LibertyPort *to_port = drive_cell->toPort();
|
|
|
|
|
float *from_slews = drive_cell->fromSlews();
|
2019-02-16 21:07:59 +01:00
|
|
|
LibertyLibrary *lib = drive_cell->library();
|
2018-09-28 17:54:21 +02:00
|
|
|
fprintf(stream_, "set_driving_cell");
|
|
|
|
|
if (tr)
|
|
|
|
|
fprintf(stream_, " %s", transRiseFallFlag(tr));
|
|
|
|
|
if (min_max)
|
|
|
|
|
fprintf(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)
|
|
|
|
|
fprintf(stream_, " -library %s", lib->name());
|
|
|
|
|
fprintf(stream_, " -lib_cell %s", cell->name());
|
2018-09-28 17:54:21 +02:00
|
|
|
if (from_port)
|
|
|
|
|
fprintf(stream_, " -from_pin {%s}",
|
|
|
|
|
from_port->name());
|
|
|
|
|
fprintf(stream_,
|
|
|
|
|
" -pin {%s} -input_transition_rise ",
|
|
|
|
|
to_port->name());
|
|
|
|
|
writeTime(from_slews[TransRiseFall::riseIndex()]);
|
|
|
|
|
fprintf(stream_, " -input_transition_fall ");
|
|
|
|
|
writeTime(from_slews[TransRiseFall::fallIndex()]);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeGetPort(port);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
RiseFallMinMax *slews = drive->slews();
|
|
|
|
|
WriteGetPort write_port(port, this);
|
|
|
|
|
writeRiseFallMinMaxTimeCmd("set_input_transition", slews, write_port);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete port_iter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeNetResistances() const
|
|
|
|
|
{
|
|
|
|
|
NetResistanceMap *net_res_map = sdc_->netResistances();
|
|
|
|
|
NetSeq nets;
|
|
|
|
|
NetResistanceMap::Iterator res_iter(net_res_map);
|
|
|
|
|
while (res_iter.hasNext()) {
|
|
|
|
|
Net *net;
|
|
|
|
|
MinMaxFloatValues values;
|
|
|
|
|
res_iter.next(net, values);
|
|
|
|
|
nets.push_back(net);
|
|
|
|
|
}
|
|
|
|
|
sort(nets, NetPathNameLess(sdc_network_));
|
|
|
|
|
NetSeq::Iterator net_iter(nets);
|
|
|
|
|
while (net_iter.hasNext()) {
|
|
|
|
|
Net *net = net_iter.next();
|
|
|
|
|
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
|
|
|
|
|
WriteSdc::writeNetResistance(Net *net,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
float res) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "set_resistance ");
|
|
|
|
|
writeResistance(res);
|
|
|
|
|
fprintf(stream_, "%s ", minMaxFlag(min_max));
|
|
|
|
|
writeGetNet(net);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeConstants() const
|
|
|
|
|
{
|
|
|
|
|
PinSeq pins;
|
|
|
|
|
sortedLogicValuePins(sdc_->logicValues(), pins);
|
|
|
|
|
PinSeq::Iterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
writeConstant(pin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeConstant(Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
const char *cmd = setConstantCmd(pin);
|
|
|
|
|
fprintf(stream_, "%s ", cmd);
|
|
|
|
|
writeGetPin(pin);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
WriteSdc::setConstantCmd(Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
LogicValue value;
|
|
|
|
|
bool exists;
|
|
|
|
|
sdc_->logicValue(pin, value, exists);
|
|
|
|
|
switch (value) {
|
2019-03-13 01:25:53 +01:00
|
|
|
case LogicValue::zero:
|
|
|
|
|
return "set_LogicValue::zero";
|
|
|
|
|
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:
|
|
|
|
|
internalError("illegal set_logic value");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeCaseAnalysis() const
|
|
|
|
|
{
|
|
|
|
|
PinSeq pins;
|
|
|
|
|
sortedLogicValuePins(sdc_->caseLogicValues(), pins);
|
|
|
|
|
PinSeq::Iterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
writeCaseAnalysis(pin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeCaseAnalysis(Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
const char *value_str = caseAnalysisValueStr(pin);
|
|
|
|
|
fprintf(stream_, "set_case_analysis %s ", value_str);
|
|
|
|
|
writeGetPin(pin);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
WriteSdc::caseAnalysisValueStr(Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
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:
|
|
|
|
|
internalError("invalid set_case_analysis value");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::sortedLogicValuePins(LogicValueMap *value_map,
|
|
|
|
|
PinSeq &pins) const
|
|
|
|
|
{
|
|
|
|
|
LogicValueMap::ConstIterator value_iter(value_map);
|
|
|
|
|
while (value_iter.hasNext()) {
|
|
|
|
|
LogicValue value;
|
|
|
|
|
const Pin *pin;
|
|
|
|
|
value_iter.next(pin, value);
|
|
|
|
|
pins.push_back(const_cast<Pin*>(pin));
|
|
|
|
|
}
|
|
|
|
|
// Sort pins.
|
|
|
|
|
sort(pins, PinPathNameLess(sdc_network_));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDeratings() const
|
|
|
|
|
{
|
|
|
|
|
DeratingFactorsGlobal *factors = sdc_->derating_factors_;
|
|
|
|
|
if (factors)
|
|
|
|
|
writeDerating(factors);
|
|
|
|
|
|
|
|
|
|
NetDeratingFactorsMap::Iterator net_iter(sdc_->net_derating_factors_);
|
|
|
|
|
while (net_iter.hasNext()) {
|
|
|
|
|
const Net *net;
|
|
|
|
|
DeratingFactorsNet *factors;
|
|
|
|
|
net_iter.next(net, factors);
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InstDeratingFactorsMap::Iterator inst_iter(sdc_->inst_derating_factors_);
|
|
|
|
|
while (inst_iter.hasNext()) {
|
|
|
|
|
const Instance *inst;
|
|
|
|
|
DeratingFactorsCell *factors;
|
|
|
|
|
inst_iter.next(inst, factors);
|
|
|
|
|
WriteGetInstance write_inst(inst, this);
|
|
|
|
|
writeDerating(factors, &write_inst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CellDeratingFactorsMap::Iterator cell_iter(sdc_->cell_derating_factors_);
|
|
|
|
|
while (cell_iter.hasNext()) {
|
|
|
|
|
const LibertyCell *cell;
|
|
|
|
|
DeratingFactorsCell *factors;
|
|
|
|
|
cell_iter.next(cell, factors);
|
|
|
|
|
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) {
|
|
|
|
|
fprintf(stream_, "set_timing_derate %s ", earlyLateFlag(early_late));
|
|
|
|
|
writeFloat(delay_value);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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()) {
|
2019-03-13 01:25:53 +01:00
|
|
|
DeratingFactors *delay_factors=factors->factors(TimingDerateType::cell_delay);
|
|
|
|
|
writeDerating(delay_factors, TimingDerateType::cell_delay, early_late, write_obj);
|
|
|
|
|
DeratingFactors *check_factors=factors->factors(TimingDerateType::cell_check);
|
|
|
|
|
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) {
|
|
|
|
|
fprintf(stream_, "set_timing_derate %s %s ",
|
|
|
|
|
type_key,
|
|
|
|
|
earlyLateFlag(early_late));
|
|
|
|
|
writeFloat(value);
|
|
|
|
|
if (write_obj) {
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
write_obj->write();
|
|
|
|
|
}
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
|
fprintf(stream_, "set_timing_derate %s %s %s ",
|
|
|
|
|
type_key,
|
|
|
|
|
earlyLateFlag(early_late),
|
|
|
|
|
clk_data_key);
|
|
|
|
|
writeFloat(value);
|
|
|
|
|
if (write_obj) {
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
write_obj->write();
|
|
|
|
|
}
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto tr : TransRiseFall::range()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
float factor;
|
|
|
|
|
bool exists;
|
|
|
|
|
factors->factor(clk_data, tr, early_late, factor, exists);
|
|
|
|
|
if (exists) {
|
|
|
|
|
fprintf(stream_, "set_timing_derate %s %s %s %s ",
|
|
|
|
|
type_key,
|
|
|
|
|
clk_data_key,
|
|
|
|
|
transRiseFallFlag(tr),
|
|
|
|
|
earlyLateFlag(early_late));
|
|
|
|
|
writeFloat(factor);
|
|
|
|
|
if (write_obj) {
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
write_obj->write();
|
|
|
|
|
}
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
timingDerateTypeKeyword(TimingDerateType type)
|
|
|
|
|
{
|
|
|
|
|
static const char *type_keys[] = {"-cell_delay","-cell_check","-net_delay"};
|
|
|
|
|
return type_keys[static_cast<int>(type)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeDesignRules() const
|
|
|
|
|
{
|
|
|
|
|
writeCommentSection("Design Rules");
|
|
|
|
|
writeMinPulseWidths();
|
|
|
|
|
writeLatchBorowLimits();
|
|
|
|
|
writeSlewLimits();
|
|
|
|
|
writeCapLimits();
|
|
|
|
|
writeFanoutLimits();
|
|
|
|
|
writeMaxArea();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeMinPulseWidths() const
|
|
|
|
|
{
|
|
|
|
|
PinMinPulseWidthMap::Iterator
|
|
|
|
|
pin_iter(sdc_->pin_min_pulse_width_map_);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
const Pin *pin;
|
|
|
|
|
RiseFallValues *min_widths;
|
|
|
|
|
pin_iter.next(pin, min_widths);
|
|
|
|
|
WriteGetPin write_obj(pin, this);
|
|
|
|
|
writeMinPulseWidths(min_widths, write_obj);
|
|
|
|
|
}
|
|
|
|
|
InstMinPulseWidthMap::Iterator
|
|
|
|
|
inst_iter(sdc_->inst_min_pulse_width_map_);
|
|
|
|
|
while (inst_iter.hasNext()) {
|
|
|
|
|
const Instance *inst;
|
|
|
|
|
RiseFallValues *min_widths;
|
|
|
|
|
inst_iter.next(inst, min_widths);
|
|
|
|
|
WriteGetInstance write_obj(inst, this);
|
|
|
|
|
writeMinPulseWidths(min_widths, write_obj);
|
|
|
|
|
}
|
|
|
|
|
ClockMinPulseWidthMap::Iterator
|
|
|
|
|
clk_iter(sdc_->clk_min_pulse_width_map_);
|
|
|
|
|
while (clk_iter.hasNext()) {
|
|
|
|
|
const Clock *clk;
|
|
|
|
|
RiseFallValues *min_widths;
|
|
|
|
|
clk_iter.next(clk, min_widths);
|
|
|
|
|
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;
|
|
|
|
|
min_widths->value(TransRiseFall::rise(), hi, hi_exists);
|
|
|
|
|
min_widths->value(TransRiseFall::fall(), low, low_exists);
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "set_min_pulse_width %s", hi_low);
|
|
|
|
|
writeTime(value);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
write_obj.write();
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeLatchBorowLimits() const
|
|
|
|
|
{
|
|
|
|
|
PinLatchBorrowLimitMap::Iterator
|
|
|
|
|
pin_iter(sdc_->pin_latch_borrow_limit_map_);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
const Pin *pin;
|
|
|
|
|
float limit;
|
|
|
|
|
pin_iter.next(pin, limit);
|
|
|
|
|
fprintf(stream_, "set_max_time_borrow ");
|
|
|
|
|
writeTime(limit);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeGetPin(pin);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
InstLatchBorrowLimitMap::Iterator
|
|
|
|
|
inst_iter(sdc_->inst_latch_borrow_limit_map_);
|
|
|
|
|
while (inst_iter.hasNext()) {
|
|
|
|
|
const Instance *inst;
|
|
|
|
|
float limit;
|
|
|
|
|
inst_iter.next(inst, limit);
|
|
|
|
|
fprintf(stream_, "set_max_time_borrow ");
|
|
|
|
|
writeTime(limit);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeGetInstance(inst);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
ClockLatchBorrowLimitMap::Iterator
|
|
|
|
|
clk_iter(sdc_->clk_latch_borrow_limit_map_);
|
|
|
|
|
while (clk_iter.hasNext()) {
|
|
|
|
|
const Clock *clk;
|
|
|
|
|
float limit;
|
|
|
|
|
clk_iter.next(clk, limit);
|
|
|
|
|
fprintf(stream_, "set_max_time_borrow ");
|
|
|
|
|
writeTime(limit);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeGetClock(clk);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeSlewLimits() const
|
|
|
|
|
{
|
|
|
|
|
const MinMax *min_max = MinMax::max();
|
|
|
|
|
float slew;
|
|
|
|
|
bool exists;
|
|
|
|
|
sdc_->slewLimit(cell_, min_max, slew, exists);
|
|
|
|
|
if (exists) {
|
|
|
|
|
fprintf(stream_, "set_max_transition ");
|
|
|
|
|
writeTime(slew);
|
|
|
|
|
fprintf(stream_, " [current_design]\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
fprintf(stream_, "set_max_transition ");
|
|
|
|
|
writeTime(slew);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeGetPort(port);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete port_iter;
|
|
|
|
|
|
|
|
|
|
ConstPinSeq pins;
|
|
|
|
|
sdc_->slewLimitPins(pins);
|
|
|
|
|
sort(pins, PinPathNameLess(network_));
|
|
|
|
|
ConstPinSeq::Iterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
const Pin *pin = pin_iter.next();
|
|
|
|
|
float slew;
|
|
|
|
|
bool exists;
|
|
|
|
|
sdc_->slewLimit(pin, min_max, slew, exists);
|
|
|
|
|
if (exists) {
|
|
|
|
|
fprintf(stream_, "set_max_transition ");
|
|
|
|
|
writeTime(slew);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeGetPin(pin);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writeClkSlewLimits();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClkSlewLimits() const
|
|
|
|
|
{
|
|
|
|
|
const MinMax *min_max = MinMax::max();
|
|
|
|
|
ClockSeq clks;
|
|
|
|
|
sdc_->sortedClocks(clks);
|
|
|
|
|
ClockSeq::Iterator clk_iter(clks);
|
|
|
|
|
while (clk_iter.hasNext()) {
|
|
|
|
|
Clock *clk = clk_iter.next();
|
|
|
|
|
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-03-13 01:25:53 +01:00
|
|
|
clk->slewLimit(TransRiseFall::rise(), PathClkOrData::clk, min_max,
|
2018-09-28 17:54:21 +02:00
|
|
|
rise_clk_limit, rise_clk_exists);
|
2019-03-13 01:25:53 +01:00
|
|
|
clk->slewLimit(TransRiseFall::fall(), PathClkOrData::clk, min_max,
|
2018-09-28 17:54:21 +02:00
|
|
|
fall_clk_limit, fall_clk_exists);
|
2019-03-13 01:25:53 +01:00
|
|
|
clk->slewLimit(TransRiseFall::rise(), PathClkOrData::data, min_max,
|
2018-09-28 17:54:21 +02:00
|
|
|
rise_data_limit, rise_data_exists);
|
2019-03-13 01:25:53 +01:00
|
|
|
clk->slewLimit(TransRiseFall::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
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "set_max_transition %s%s", clk_data, rise_fall);
|
|
|
|
|
writeTime(limit);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeGetClock(clk);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
fprintf(stream_, "%s ", cmd);
|
|
|
|
|
writeCapacitance(cap);
|
|
|
|
|
fprintf(stream_, " [current_design]\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PortCapLimitMap::Iterator port_iter(sdc_->port_cap_limit_map_);
|
|
|
|
|
while (port_iter.hasNext()) {
|
|
|
|
|
Port *port;
|
|
|
|
|
MinMaxFloatValues values;
|
|
|
|
|
port_iter.next(port, values);
|
|
|
|
|
float cap;
|
|
|
|
|
bool exists;
|
|
|
|
|
values.value(min_max, cap, exists);
|
|
|
|
|
if (exists) {
|
|
|
|
|
fprintf(stream_, "%s ", cmd);
|
|
|
|
|
writeCapacitance(cap);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeGetPort(port);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PinCapLimitMap::Iterator pin_iter(sdc_->pin_cap_limit_map_);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin;
|
|
|
|
|
MinMaxFloatValues values;
|
|
|
|
|
pin_iter.next(pin, values);
|
|
|
|
|
float cap;
|
|
|
|
|
bool exists;
|
|
|
|
|
values.value(min_max, cap, exists);
|
|
|
|
|
if (exists) {
|
|
|
|
|
fprintf(stream_, "%s ", cmd);
|
|
|
|
|
writeCapacitance(cap);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeGetPin(pin);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeMaxArea() const
|
|
|
|
|
{
|
|
|
|
|
float max_area = sdc_->maxArea();
|
|
|
|
|
if (max_area > 0.0) {
|
|
|
|
|
fprintf(stream_, "set_max_area ");
|
|
|
|
|
writeFloat(max_area);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
fprintf(stream_, "%s ", cmd);
|
|
|
|
|
writeFloat(fanout);
|
|
|
|
|
fprintf(stream_, " [current_design]\n");
|
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
|
fprintf(stream_, "%s ", cmd);
|
|
|
|
|
writeFloat(fanout);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeGetPort(port);
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete port_iter;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeVariables() const
|
|
|
|
|
{
|
|
|
|
|
if (sdc_->propagateAllClocks()) {
|
2019-02-16 21:07:59 +01:00
|
|
|
if (compatible_)
|
2018-09-28 17:54:21 +02:00
|
|
|
fprintf(stream_, "set timing_all_clocks_propagated true\n");
|
2019-02-16 21:07:59 +01:00
|
|
|
else
|
|
|
|
|
fprintf(stream_, "set sta_propagate_all_clocks 1\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
if (sdc_->presetClrArcsEnabled()) {
|
2019-02-16 21:07:59 +01:00
|
|
|
if (compatible_)
|
2018-09-28 17:54:21 +02:00
|
|
|
fprintf(stream_, "set timing_enable_preset_clear_arcs true\n");
|
2019-02-16 21:07:59 +01:00
|
|
|
else
|
|
|
|
|
fprintf(stream_, "set sta_preset_clear_arcs_enabled 1\n");
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetTimingArcsOfOjbects(LibertyCell *cell) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "[%s -of_objects ", getTimingArcsCmd());
|
|
|
|
|
writeGetLibCell(cell);
|
|
|
|
|
fprintf(stream_, "]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "[%s -from ", getTimingArcsCmd());
|
|
|
|
|
Vertex *from_vertex = edge->from(graph_);
|
|
|
|
|
writeGetPin(from_vertex->pin());
|
|
|
|
|
fprintf(stream_, " -to ");
|
|
|
|
|
Vertex *to_vertex = edge->to(graph_);
|
|
|
|
|
writeGetPin(to_vertex->pin());
|
|
|
|
|
if (filter)
|
|
|
|
|
fprintf(stream_, " -filter {%s}", filter);
|
|
|
|
|
fprintf(stream_, "]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
WriteSdc::getTimingArcsCmd() const
|
|
|
|
|
{
|
2019-02-16 21:07:59 +01:00
|
|
|
return compatible_ ? "get_timing_arcs" : "get_timing_edges";
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetLibCell(const LibertyCell *cell) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "[get_lib_cells {%s/%s}]",
|
|
|
|
|
cell->libertyLibrary()->name(),
|
|
|
|
|
cell->name());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetLibPin(const LibertyPort *port) const
|
|
|
|
|
{
|
|
|
|
|
LibertyCell *cell = port->libertyCell();
|
|
|
|
|
LibertyLibrary *lib = cell->libertyLibrary();
|
|
|
|
|
fprintf(stream_, "[get_lib_pins {%s/%s/%s}]",
|
|
|
|
|
lib->name(),
|
|
|
|
|
cell->name(),
|
|
|
|
|
port->name());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetClocks(ClockSet *clks) const
|
|
|
|
|
{
|
|
|
|
|
bool first = true;
|
|
|
|
|
bool multiple = clks->size() > 1;
|
|
|
|
|
if (multiple)
|
|
|
|
|
fprintf(stream_, "[list ");
|
|
|
|
|
writeGetClocks(clks, multiple, first);
|
|
|
|
|
if (multiple)
|
|
|
|
|
fprintf(stream_, "]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetClocks(ClockSet *clks,
|
|
|
|
|
bool multiple,
|
|
|
|
|
bool &first) const
|
|
|
|
|
{
|
|
|
|
|
ClockSeq clks1;
|
|
|
|
|
sortClockSet(clks, clks1);
|
|
|
|
|
ClockSeq::Iterator clk_iter(clks1);
|
|
|
|
|
while (clk_iter.hasNext()) {
|
|
|
|
|
Clock *clk = clk_iter.next();
|
|
|
|
|
if (multiple && !first)
|
|
|
|
|
fprintf(stream_, "\\\n ");
|
|
|
|
|
writeGetClock(clk);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetClock(const Clock *clk) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "[get_clocks {%s}]",
|
|
|
|
|
clk->name());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetPort(const Port *port) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "[get_ports {%s}]", sdc_network_->name(port));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetPins(PinSet *pins) const
|
|
|
|
|
{
|
|
|
|
|
PinSeq pins1;
|
|
|
|
|
sortPinSet(pins, sdc_network_, pins1);
|
|
|
|
|
writeGetPins(&pins1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetPins(PinSeq *pins) const
|
|
|
|
|
{
|
|
|
|
|
bool multiple = pins->size() > 1;
|
|
|
|
|
if (multiple)
|
|
|
|
|
fprintf(stream_, "[list ");
|
|
|
|
|
PinSeq::Iterator pin_iter(pins);
|
|
|
|
|
bool first = true;
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
if (multiple && !first)
|
|
|
|
|
fprintf(stream_, "\\\n ");
|
|
|
|
|
writeGetPin(pin);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
if (multiple)
|
|
|
|
|
fprintf(stream_, "]");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetPin(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
if (sdc_network_->instance(pin) == instance_)
|
|
|
|
|
fprintf(stream_, "[get_ports {%s}]", sdc_network_->portName(pin));
|
|
|
|
|
else
|
|
|
|
|
fprintf(stream_, "[get_pins {%s}]", pathName(pin));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetNet(const Net *net) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "[get_nets {%s}]", pathName(net));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeGetInstance(const Instance *inst) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "[get_cells {%s}]", pathName(inst));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
fprintf(stream_, "# %s\n", line);
|
|
|
|
|
writeCommentSeparator();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeCommentSeparator() const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "###############################################################################\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeRiseFallMinMaxTimeCmd(const char *sdc_cmd,
|
|
|
|
|
RiseFallMinMax *values,
|
|
|
|
|
WriteSdcObject &write_object) const
|
|
|
|
|
{
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, values, units_->timeUnit()->scale(),
|
|
|
|
|
write_object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeRiseFallMinMaxCapCmd(const char *sdc_cmd,
|
|
|
|
|
RiseFallMinMax *values,
|
|
|
|
|
WriteSdcObject &write_object) const
|
|
|
|
|
{
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, values, units_->capacitanceUnit()->scale(),
|
|
|
|
|
write_object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd,
|
|
|
|
|
RiseFallMinMax *values,
|
|
|
|
|
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;
|
|
|
|
|
values->value(TransRiseFall::fall(), MinMax::min(),
|
|
|
|
|
fall_min, fall_min_exists);
|
|
|
|
|
values->value(TransRiseFall::fall(), MinMax::max(),
|
|
|
|
|
fall_max, fall_max_exists);
|
|
|
|
|
values->value(TransRiseFall::rise(), MinMax::min(),
|
|
|
|
|
rise_min, rise_min_exists);
|
|
|
|
|
values->value(TransRiseFall::rise(), MinMax::max(),
|
|
|
|
|
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,
|
|
|
|
|
TransRiseFallBoth::riseFall(), MinMaxAll::all(),
|
|
|
|
|
write_object);
|
|
|
|
|
}
|
|
|
|
|
else if (rise_min == fall_min
|
|
|
|
|
&& rise_max == fall_max) {
|
|
|
|
|
// rise/fall match.
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale,
|
|
|
|
|
TransRiseFallBoth::riseFall(), MinMaxAll::min(),
|
|
|
|
|
write_object);
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, rise_max, scale,
|
|
|
|
|
TransRiseFallBoth::riseFall(), MinMaxAll::max(),
|
|
|
|
|
write_object);
|
|
|
|
|
}
|
|
|
|
|
else if (rise_min == rise_max
|
|
|
|
|
&& fall_min == fall_max) {
|
|
|
|
|
// min/max match.
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale,
|
|
|
|
|
TransRiseFallBoth::rise(), MinMaxAll::all(),
|
|
|
|
|
write_object);
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, fall_min, scale,
|
|
|
|
|
TransRiseFallBoth::fall(), MinMaxAll::all(),
|
|
|
|
|
write_object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (rise_min_exists)
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, rise_min, scale,
|
|
|
|
|
TransRiseFallBoth::rise(), MinMaxAll::min(),
|
|
|
|
|
write_object);
|
|
|
|
|
if (rise_max_exists)
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, rise_max, scale,
|
|
|
|
|
TransRiseFallBoth::rise(), MinMaxAll::max(),
|
|
|
|
|
write_object);
|
|
|
|
|
if (fall_min_exists)
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, fall_min, scale,
|
|
|
|
|
TransRiseFallBoth::fall(), MinMaxAll::min(),
|
|
|
|
|
write_object);
|
|
|
|
|
if (fall_max_exists)
|
|
|
|
|
writeRiseFallMinMaxCmd(sdc_cmd, fall_max, scale,
|
|
|
|
|
TransRiseFallBoth::fall(), MinMaxAll::max(),
|
|
|
|
|
write_object);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeRiseFallMinMaxCmd(const char *sdc_cmd,
|
|
|
|
|
float value,
|
|
|
|
|
float scale,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
WriteSdcObject &write_object) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "%s%s%s ",
|
|
|
|
|
sdc_cmd,
|
|
|
|
|
transRiseFallFlag(tr),
|
|
|
|
|
minMaxFlag(min_max));
|
|
|
|
|
writeFloat(value / scale);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
write_object.write();
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeClockKey(const Clock *clk) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, " -clock ");
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "%s%s ",
|
|
|
|
|
sdc_cmd,
|
|
|
|
|
minMaxFlag(min_max));
|
|
|
|
|
writeFloat(value / scale);
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
write_object.write();
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "%s%s ",
|
|
|
|
|
sdc_cmd,
|
|
|
|
|
minMaxFlag(min_max));
|
|
|
|
|
fprintf(stream_, "%d ", value);
|
|
|
|
|
write_object.write();
|
|
|
|
|
fprintf(stream_, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "%.*f", digits_, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeTime(float time) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "%.*f", digits_, scaleTime(time));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeCapacitance(float cap) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "%.*f", digits_, scaleCapacitance(cap));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeResistance(float res) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "%.*f", digits_, scaleResistance(res));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeFloatSeq(FloatSeq *floats,
|
|
|
|
|
float scale) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "{");
|
|
|
|
|
FloatSeq::ConstIterator iter(floats);
|
|
|
|
|
bool first = true;
|
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
|
float flt = iter.next();
|
|
|
|
|
if (!first)
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
writeFloat(flt * scale);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
fprintf(stream_, "}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeIntSeq(IntSeq *ints) const
|
|
|
|
|
{
|
|
|
|
|
fprintf(stream_, "{");
|
|
|
|
|
IntSeq::ConstIterator iter(ints);
|
|
|
|
|
bool first = true;
|
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
|
int i = iter.next();
|
|
|
|
|
if (!first)
|
|
|
|
|
fprintf(stream_, " ");
|
|
|
|
|
fprintf(stream_, "%d", i);
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
fprintf(stream_, "}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
transRiseFallFlag(const TransRiseFall *tr)
|
|
|
|
|
{
|
|
|
|
|
return (tr == TransRiseFall::rise()) ? "-rise" : "-fall";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
transRiseFallFlag(const TransRiseFallBoth *tr)
|
|
|
|
|
{
|
|
|
|
|
if (tr == TransRiseFallBoth::rise())
|
|
|
|
|
return " -rise";
|
|
|
|
|
else if (tr == TransRiseFallBoth::fall())
|
|
|
|
|
return " -fall";
|
|
|
|
|
else if (tr == TransRiseFallBoth::riseFall())
|
|
|
|
|
return "";
|
|
|
|
|
else {
|
|
|
|
|
internalError("unknown transition");
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
minMaxFlag(const MinMaxAll *min_max)
|
|
|
|
|
{
|
|
|
|
|
if (min_max == MinMaxAll::all())
|
|
|
|
|
return "";
|
|
|
|
|
else if (min_max == MinMaxAll::min())
|
|
|
|
|
return " -min";
|
|
|
|
|
else if (min_max == MinMaxAll::max())
|
|
|
|
|
return " -max";
|
|
|
|
|
else {
|
|
|
|
|
internalError("unknown MinMaxAll");
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
minMaxFlag(const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
if (min_max == MinMax::min())
|
|
|
|
|
return " -min";
|
|
|
|
|
else if (min_max == MinMax::max())
|
|
|
|
|
return " -max";
|
|
|
|
|
else {
|
|
|
|
|
internalError("unknown MinMax");
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
earlyLateFlag(const MinMax *early_late)
|
|
|
|
|
{
|
|
|
|
|
if (early_late == MinMax::min())
|
|
|
|
|
return "-early";
|
|
|
|
|
else if (early_late == MinMax::max())
|
|
|
|
|
return "-late";
|
|
|
|
|
else {
|
|
|
|
|
internalError("unknown EarlyLate");
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeSetupHoldFlag(const MinMaxAll *min_max) const
|
|
|
|
|
{
|
|
|
|
|
if (min_max == MinMaxAll::min())
|
|
|
|
|
fprintf(stream_, " -hold");
|
|
|
|
|
else if (min_max == MinMaxAll::max())
|
|
|
|
|
fprintf(stream_, " -setup");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
setupHoldFlag(const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
if (min_max == MinMax::min())
|
|
|
|
|
return " -hold";
|
|
|
|
|
else if (min_max == MinMax::max())
|
|
|
|
|
return " -setup";
|
|
|
|
|
else {
|
|
|
|
|
internalError("unknown MinMax");
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
WriteSdc::writeCmdComment(SdcCmdComment *cmd) const
|
|
|
|
|
{
|
|
|
|
|
const char *comment = cmd->comment();
|
|
|
|
|
if (comment) {
|
|
|
|
|
fprintf(stream_, " -comment {%s}", comment);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|