write_timing_model min/max_clock_tree_path
commit 327766984f2beedc5c83b0acdab0df48fb61d2ba
Author: James Cherry <cherry@parallaxsw.com>
Date: Wed Oct 4 11:09:38 2023 -0700
write_timing_model min/max_clock_tree_path
Signed-off-by: James Cherry <cherry@parallaxsw.com>
commit 675c1ff6d8cfa90a0d1bd5c8960a8b23e75056a3
Author: James Cherry <cherry@parallaxsw.com>
Date: Wed Oct 4 09:02:39 2023 -0700
min/max_clock_tree_path
Signed-off-by: James Cherry <cherry@parallaxsw.com>
Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
parent
dad8c74f75
commit
76d6dd1dae
|
|
@ -191,7 +191,7 @@ Graph::makePortInstanceEdges(const Instance *inst,
|
||||||
if ((from_to_port == nullptr
|
if ((from_to_port == nullptr
|
||||||
|| from_port == from_to_port
|
|| from_port == from_to_port
|
||||||
|| to_port == from_to_port)
|
|| to_port == from_to_port)
|
||||||
&& filterEdge(arc_set)) {
|
&& from_port) {
|
||||||
Pin *from_pin = network_->findPin(inst, from_port);
|
Pin *from_pin = network_->findPin(inst, from_port);
|
||||||
Pin *to_pin = network_->findPin(inst, to_port);
|
Pin *to_pin = network_->findPin(inst, to_port);
|
||||||
if (from_pin && to_pin) {
|
if (from_pin && to_pin) {
|
||||||
|
|
|
||||||
|
|
@ -232,8 +232,6 @@ protected:
|
||||||
Edge *edge);
|
Edge *edge);
|
||||||
void removeDelays();
|
void removeDelays();
|
||||||
void removeDelayAnnotated(Edge *edge);
|
void removeDelayAnnotated(Edge *edge);
|
||||||
// User defined predicate to filter graph edges for liberty timing arcs.
|
|
||||||
virtual bool filterEdge(TimingArcSet *) const { return true; }
|
|
||||||
|
|
||||||
VertexTable *vertices_;
|
VertexTable *vertices_;
|
||||||
EdgeTable *edges_;
|
EdgeTable *edges_;
|
||||||
|
|
|
||||||
|
|
@ -110,8 +110,10 @@ public:
|
||||||
bool exists = exists_[mm_index];
|
bool exists = exists_[mm_index];
|
||||||
if (exists)
|
if (exists)
|
||||||
return values_[mm_index];
|
return values_[mm_index];
|
||||||
else
|
else {
|
||||||
criticalError(226, "uninitialized value reference");
|
criticalError(226, "uninitialized value reference");
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include "Map.hh"
|
#include "Map.hh"
|
||||||
#include "UnorderedMap.hh"
|
#include "UnorderedMap.hh"
|
||||||
#include "StringSet.hh"
|
#include "StringSet.hh"
|
||||||
|
#include "MinMaxValues.hh"
|
||||||
#include "Delay.hh"
|
#include "Delay.hh"
|
||||||
#include "NetworkClass.hh"
|
#include "NetworkClass.hh"
|
||||||
#include "GraphClass.hh"
|
#include "GraphClass.hh"
|
||||||
|
|
@ -113,6 +114,7 @@ typedef Vector<PathVertex> PathVertexSeq;
|
||||||
typedef Vector<Slack> SlackSeq;
|
typedef Vector<Slack> SlackSeq;
|
||||||
typedef Delay Crpr;
|
typedef Delay Crpr;
|
||||||
typedef Vector<PathRef> PathRefSeq;
|
typedef Vector<PathRef> PathRefSeq;
|
||||||
|
typedef MinMaxValues<float> ClkDelays[RiseFall::index_count][RiseFall::index_count];
|
||||||
|
|
||||||
enum class ReportPathFormat { full,
|
enum class ReportPathFormat { full,
|
||||||
full_clock,
|
full_clock,
|
||||||
|
|
|
||||||
|
|
@ -900,12 +900,6 @@ public:
|
||||||
void setReportPathDigits(int digits);
|
void setReportPathDigits(int digits);
|
||||||
void setReportPathNoSplit(bool no_split);
|
void setReportPathNoSplit(bool no_split);
|
||||||
void setReportPathSigmas(bool report_sigmas);
|
void setReportPathSigmas(bool report_sigmas);
|
||||||
// Report clk skews for clks.
|
|
||||||
void reportClkSkew(ClockSet *clks,
|
|
||||||
const Corner *corner,
|
|
||||||
const SetupHold *setup_hold,
|
|
||||||
int digits);
|
|
||||||
float findWorstClkSkew(const SetupHold *setup_hold);
|
|
||||||
// Header above reportPathEnd results.
|
// Header above reportPathEnd results.
|
||||||
void reportPathEndHeader();
|
void reportPathEndHeader();
|
||||||
// Footer below reportPathEnd results.
|
// Footer below reportPathEnd results.
|
||||||
|
|
@ -919,6 +913,18 @@ public:
|
||||||
void reportPathEnds(PathEndSeq *ends);
|
void reportPathEnds(PathEndSeq *ends);
|
||||||
ReportPath *reportPath() { return report_path_; }
|
ReportPath *reportPath() { return report_path_; }
|
||||||
void reportPath(Path *path);
|
void reportPath(Path *path);
|
||||||
|
|
||||||
|
// Report clk skews for clks.
|
||||||
|
void reportClkSkew(ClockSet *clks,
|
||||||
|
const Corner *corner,
|
||||||
|
const SetupHold *setup_hold,
|
||||||
|
int digits);
|
||||||
|
float findWorstClkSkew(const SetupHold *setup_hold);
|
||||||
|
// Find min/max/rise/fall delays for clk.
|
||||||
|
void findClkDelays(const Clock *clk,
|
||||||
|
// Return values.
|
||||||
|
ClkDelays &delays);
|
||||||
|
|
||||||
// Update arrival times for all pins.
|
// Update arrival times for all pins.
|
||||||
// If necessary updateTiming propagates arrivals around latch
|
// If necessary updateTiming propagates arrivals around latch
|
||||||
// loops until the arrivals converge.
|
// loops until the arrivals converge.
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,7 @@ public:
|
||||||
TableModel(TablePtr table,
|
TableModel(TablePtr table,
|
||||||
TableTemplate *tbl_template,
|
TableTemplate *tbl_template,
|
||||||
ScaleFactorType scale_factor_type,
|
ScaleFactorType scale_factor_type,
|
||||||
RiseFall *rf);
|
const RiseFall *rf);
|
||||||
void setScaleFactorType(ScaleFactorType type);
|
void setScaleFactorType(ScaleFactorType type);
|
||||||
int order() const;
|
int order() const;
|
||||||
TableTemplate *tblTemplate() const { return tbl_template_; }
|
TableTemplate *tblTemplate() const { return tbl_template_; }
|
||||||
|
|
|
||||||
|
|
@ -105,8 +105,8 @@ public:
|
||||||
void setModeName(const char *name);
|
void setModeName(const char *name);
|
||||||
const char *modeValue() const { return mode_value_; }
|
const char *modeValue() const { return mode_value_; }
|
||||||
void setModeValue(const char *value);
|
void setModeValue(const char *value);
|
||||||
TimingModel *model(RiseFall *rf) const;
|
TimingModel *model(const RiseFall *rf) const;
|
||||||
void setModel(RiseFall *rf,
|
void setModel(const RiseFall *rf,
|
||||||
TimingModel *model);
|
TimingModel *model);
|
||||||
float ocvArcDepth() const { return ocv_arc_depth_; }
|
float ocvArcDepth() const { return ocv_arc_depth_; }
|
||||||
void setOcvArcDepth(float depth);
|
void setOcvArcDepth(float depth);
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ public:
|
||||||
static TimingRole *dataCheckHold() { return data_check_hold_; }
|
static TimingRole *dataCheckHold() { return data_check_hold_; }
|
||||||
static TimingRole *nonSeqSetup() { return non_seq_setup_; }
|
static TimingRole *nonSeqSetup() { return non_seq_setup_; }
|
||||||
static TimingRole *nonSeqHold() { return non_seq_hold_; }
|
static TimingRole *nonSeqHold() { return non_seq_hold_; }
|
||||||
|
static TimingRole *clockTreePathMin() { return clock_tree_path_min_; }
|
||||||
|
static TimingRole *clockTreePathMax() { return clock_tree_path_max_; }
|
||||||
const char *asString() const { return name_; }
|
const char *asString() const { return name_; }
|
||||||
int index() const { return index_; }
|
int index() const { return index_; }
|
||||||
bool isWire() const;
|
bool isWire() const;
|
||||||
|
|
@ -125,6 +127,8 @@ private:
|
||||||
static TimingRole *data_check_hold_;
|
static TimingRole *data_check_hold_;
|
||||||
static TimingRole *non_seq_setup_;
|
static TimingRole *non_seq_setup_;
|
||||||
static TimingRole *non_seq_hold_;
|
static TimingRole *non_seq_hold_;
|
||||||
|
static TimingRole *clock_tree_path_min_;
|
||||||
|
static TimingRole *clock_tree_path_max_;
|
||||||
static TimingRoleMap timing_roles_;
|
static TimingRoleMap timing_roles_;
|
||||||
|
|
||||||
friend class TimingRoleLess;
|
friend class TimingRoleLess;
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,9 @@ public:
|
||||||
RiseFall *opposite() const;
|
RiseFall *opposite() const;
|
||||||
|
|
||||||
// for range support.
|
// for range support.
|
||||||
// for (auto tr : RiseFall::range()) {}
|
// for (auto rf : RiseFall::range()) {}
|
||||||
static const std::array<RiseFall*, 2> &range() { return range_; }
|
static const std::array<RiseFall*, 2> &range() { return range_; }
|
||||||
// for (auto tr_index : RiseFall::rangeIndex()) {}
|
// for (auto rf_index : RiseFall::rangeIndex()) {}
|
||||||
static const std::array<int, 2> &rangeIndex() { return range_index_; }
|
static const std::array<int, 2> &rangeIndex() { return range_index_; }
|
||||||
static const int index_count = 2;
|
static const int index_count = 2;
|
||||||
static const int index_max = (index_count - 1);
|
static const int index_max = (index_count - 1);
|
||||||
|
|
|
||||||
|
|
@ -2352,15 +2352,16 @@ bool
|
||||||
LibertyPort::less(const LibertyPort *port1,
|
LibertyPort::less(const LibertyPort *port1,
|
||||||
const LibertyPort *port2)
|
const LibertyPort *port2)
|
||||||
{
|
{
|
||||||
|
if (port1 == nullptr && port2 != nullptr)
|
||||||
|
return true;
|
||||||
|
if (port1 != nullptr && port2 == nullptr)
|
||||||
|
return false;
|
||||||
const char *name1 = port1->name();
|
const char *name1 = port1->name();
|
||||||
const char *name2 = port2->name();
|
const char *name2 = port2->name();
|
||||||
if (stringEq(name1, name2)) {
|
if (stringEq(name1, name2)) {
|
||||||
PortDirection *dir1 = port1->direction();
|
PortDirection *dir1 = port1->direction();
|
||||||
PortDirection *dir2 = port2->direction();
|
PortDirection *dir2 = port2->direction();
|
||||||
if (dir1 == dir2) {
|
return dir1->index() < dir2->index();
|
||||||
}
|
|
||||||
else
|
|
||||||
return dir1->index() < dir2->index();
|
|
||||||
}
|
}
|
||||||
return stringLess(name1, name2);
|
return stringLess(name1, name2);
|
||||||
}
|
}
|
||||||
|
|
@ -2588,8 +2589,8 @@ bool
|
||||||
LibertyPortPairLess::operator()(const LibertyPortPair &pair1,
|
LibertyPortPairLess::operator()(const LibertyPortPair &pair1,
|
||||||
const LibertyPortPair &pair2) const
|
const LibertyPortPair &pair2) const
|
||||||
{
|
{
|
||||||
ObjectId id1 = pair1.first->id();
|
ObjectId id1 = pair1.first ? pair1.first->id() : 0;
|
||||||
ObjectId id2 = pair2.first->id();
|
ObjectId id2 = pair2.first ? pair2.first->id() : 0;
|
||||||
return id1 < id2
|
return id1 < id2
|
||||||
|| (id1 == id2
|
|| (id1 == id2
|
||||||
&& pair1.second->id() < pair2.second->id());
|
&& pair1.second->id() < pair2.second->id());
|
||||||
|
|
|
||||||
|
|
@ -281,6 +281,14 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell,
|
||||||
RiseFall::fall(),
|
RiseFall::fall(),
|
||||||
TimingRole::nonSeqHold(),
|
TimingRole::nonSeqHold(),
|
||||||
attrs);
|
attrs);
|
||||||
|
case TimingType::min_clock_tree_path:
|
||||||
|
return makeClockTreePathArcs(cell, to_port, related_out,
|
||||||
|
TimingRole::clockTreePathMin(),
|
||||||
|
attrs);
|
||||||
|
case TimingType::max_clock_tree_path:
|
||||||
|
return makeClockTreePathArcs(cell, to_port, related_out,
|
||||||
|
TimingRole::clockTreePathMax(),
|
||||||
|
attrs);
|
||||||
case TimingType::min_pulse_width:
|
case TimingType::min_pulse_width:
|
||||||
case TimingType::minimum_period:
|
case TimingType::minimum_period:
|
||||||
case TimingType::nochange_high_high:
|
case TimingType::nochange_high_high:
|
||||||
|
|
@ -289,8 +297,6 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell,
|
||||||
case TimingType::nochange_low_low:
|
case TimingType::nochange_low_low:
|
||||||
case TimingType::retaining_time:
|
case TimingType::retaining_time:
|
||||||
case TimingType::unknown:
|
case TimingType::unknown:
|
||||||
case TimingType::min_clock_tree_path:
|
|
||||||
case TimingType::max_clock_tree_path:
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
// Prevent warnings from lame compilers.
|
// Prevent warnings from lame compilers.
|
||||||
|
|
@ -647,6 +653,23 @@ LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell,
|
||||||
return arc_set;
|
return arc_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimingArcSet *
|
||||||
|
LibertyBuilder::makeClockTreePathArcs(LibertyCell *cell,
|
||||||
|
LibertyPort *to_port,
|
||||||
|
LibertyPort *related_out,
|
||||||
|
TimingRole *role,
|
||||||
|
TimingArcAttrsPtr attrs)
|
||||||
|
{
|
||||||
|
TimingArcSet *arc_set = makeTimingArcSet(cell, nullptr, to_port,
|
||||||
|
related_out, role, attrs);
|
||||||
|
for (auto to_rf : RiseFall::range()) {
|
||||||
|
TimingModel *model = attrs->model(to_rf);
|
||||||
|
if (model)
|
||||||
|
makeTimingArc(arc_set, nullptr, to_rf, model);
|
||||||
|
}
|
||||||
|
return arc_set;
|
||||||
|
}
|
||||||
|
|
||||||
TimingArcSet *
|
TimingArcSet *
|
||||||
LibertyBuilder::makeTimingArcSet(LibertyCell *cell,
|
LibertyBuilder::makeTimingArcSet(LibertyCell *cell,
|
||||||
LibertyPort *from,
|
LibertyPort *from,
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,11 @@ public:
|
||||||
bool to_rise,
|
bool to_rise,
|
||||||
bool to_fall,
|
bool to_fall,
|
||||||
TimingArcAttrsPtr attrs);
|
TimingArcAttrsPtr attrs);
|
||||||
|
TimingArcSet *makeClockTreePathArcs(LibertyCell *cell,
|
||||||
|
LibertyPort *to_port,
|
||||||
|
LibertyPort *related_out,
|
||||||
|
TimingRole *role,
|
||||||
|
TimingArcAttrsPtr attrs);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ConcretePort *makeBusPort(const char *name,
|
ConcretePort *makeBusPort(const char *name,
|
||||||
|
|
|
||||||
|
|
@ -2195,6 +2195,8 @@ LibertyReader::makeTimingArcs(LibertyPort *to_port,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
makeTimingArcs(to_port, related_out_port, timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -2363,6 +2365,26 @@ LibertyReader::makeTimingArcs(const char *from_port_name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LibertyReader::makeTimingArcs(LibertyPort *to_port,
|
||||||
|
LibertyPort *related_out_port,
|
||||||
|
TimingGroup *timing)
|
||||||
|
{
|
||||||
|
if (to_port->hasMembers()) {
|
||||||
|
LibertyPortMemberIterator bit_iter(to_port);
|
||||||
|
while (bit_iter.hasNext()) {
|
||||||
|
LibertyPort *to_port_bit = bit_iter.next();
|
||||||
|
builder_.makeTimingArcs(cell_, nullptr, to_port_bit,
|
||||||
|
related_out_port, timing->attrs(),
|
||||||
|
timing->line());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
builder_.makeTimingArcs(cell_, nullptr, to_port,
|
||||||
|
related_out_port, timing->attrs(),
|
||||||
|
timing->line());
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,9 @@ public:
|
||||||
LibertyPort *to_port,
|
LibertyPort *to_port,
|
||||||
LibertyPort *related_out_port,
|
LibertyPort *related_out_port,
|
||||||
TimingGroup *timing);
|
TimingGroup *timing);
|
||||||
|
virtual void makeTimingArcs(LibertyPort *to_port,
|
||||||
|
LibertyPort *related_out_port,
|
||||||
|
TimingGroup *timing);
|
||||||
|
|
||||||
virtual void visitClockGatingIntegratedCell(LibertyAttr *attr);
|
virtual void visitClockGatingIntegratedCell(LibertyAttr *attr);
|
||||||
virtual void visitArea(LibertyAttr *attr);
|
virtual void visitArea(LibertyAttr *attr);
|
||||||
|
|
|
||||||
|
|
@ -364,7 +364,8 @@ void
|
||||||
LibertyWriter::writeTimingArcSet(const TimingArcSet *arc_set)
|
LibertyWriter::writeTimingArcSet(const TimingArcSet *arc_set)
|
||||||
{
|
{
|
||||||
fprintf(stream_, " timing() {\n");
|
fprintf(stream_, " timing() {\n");
|
||||||
fprintf(stream_, " related_pin : \"%s\";\n", arc_set->from()->name());
|
if (arc_set->from())
|
||||||
|
fprintf(stream_, " related_pin : \"%s\";\n", arc_set->from()->name());
|
||||||
TimingSense sense = arc_set->sense();
|
TimingSense sense = arc_set->sense();
|
||||||
if (sense != TimingSense::unknown
|
if (sense != TimingSense::unknown
|
||||||
&& sense != TimingSense::non_unate)
|
&& sense != TimingSense::non_unate)
|
||||||
|
|
@ -398,11 +399,14 @@ LibertyWriter::writeTimingModels(const TimingArc *arc,
|
||||||
fprintf(stream_, " }\n");
|
fprintf(stream_, " }\n");
|
||||||
|
|
||||||
const TableModel *slew_model = gate_model->slewModel();
|
const TableModel *slew_model = gate_model->slewModel();
|
||||||
template_name = slew_model->tblTemplate()->name();
|
if (slew_model) {
|
||||||
fprintf(stream_, " %s_transition(%s) {\n", rf->name(), template_name);
|
template_name = slew_model->tblTemplate()->name();
|
||||||
writeTableModel(slew_model);
|
fprintf(stream_, " %s_transition(%s) {\n", rf->name(), template_name);
|
||||||
fprintf(stream_, " }\n");
|
writeTableModel(slew_model);
|
||||||
} else if (check_model) {
|
fprintf(stream_, " }\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (check_model) {
|
||||||
const TableModel *model = check_model->model();
|
const TableModel *model = check_model->model();
|
||||||
const char *template_name = model->tblTemplate()->name();
|
const char *template_name = model->tblTemplate()->name();
|
||||||
fprintf(stream_, " %s_constraint(%s) {\n", rf->name(), template_name);
|
fprintf(stream_, " %s_constraint(%s) {\n", rf->name(), template_name);
|
||||||
|
|
@ -569,11 +573,15 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
|
||||||
else
|
else
|
||||||
return "non_seq_hold_falling";
|
return "non_seq_hold_falling";
|
||||||
}
|
}
|
||||||
|
else if (role == TimingRole::clockTreePathMin())
|
||||||
|
return "min_clock_tree_path";
|
||||||
|
else if (role == TimingRole::clockTreePathMax())
|
||||||
|
return "max_clock_tree_path";
|
||||||
else {
|
else {
|
||||||
report_->error(703, "%s/%s/%s timing arc type %s not supported.",
|
report_->error(703, "%s/%s/%s timing arc type %s not supported.",
|
||||||
library_->name(),
|
library_->name(),
|
||||||
arc_set->from()->libertyCell()->name(),
|
arc_set->to()->libertyCell()->name(),
|
||||||
arc_set->from()->name(),
|
arc_set->to()->name(),
|
||||||
role->asString());
|
role->asString());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -611,7 +611,7 @@ CheckTableModel::checkAxis(TableAxisPtr axis)
|
||||||
TableModel::TableModel(TablePtr table,
|
TableModel::TableModel(TablePtr table,
|
||||||
TableTemplate *tbl_template,
|
TableTemplate *tbl_template,
|
||||||
ScaleFactorType scale_factor_type,
|
ScaleFactorType scale_factor_type,
|
||||||
RiseFall *rf) :
|
const RiseFall *rf) :
|
||||||
table_(table),
|
table_(table),
|
||||||
tbl_template_(tbl_template),
|
tbl_template_(tbl_template),
|
||||||
scale_factor_type_(int(scale_factor_type)),
|
scale_factor_type_(int(scale_factor_type)),
|
||||||
|
|
|
||||||
|
|
@ -127,13 +127,13 @@ TimingArcAttrs::setModeValue(const char *value)
|
||||||
}
|
}
|
||||||
|
|
||||||
TimingModel *
|
TimingModel *
|
||||||
TimingArcAttrs::model(RiseFall *rf) const
|
TimingArcAttrs::model(const RiseFall *rf) const
|
||||||
{
|
{
|
||||||
return models_[rf->index()];
|
return models_[rf->index()];
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimingArcAttrs::setModel(RiseFall *rf,
|
TimingArcAttrs::setModel(const RiseFall *rf,
|
||||||
TimingModel *model)
|
TimingModel *model)
|
||||||
{
|
{
|
||||||
models_[rf->index()] = model;
|
models_[rf->index()] = model;
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ TimingRole *TimingRole::data_check_setup_;
|
||||||
TimingRole *TimingRole::data_check_hold_;
|
TimingRole *TimingRole::data_check_hold_;
|
||||||
TimingRole *TimingRole::non_seq_setup_;
|
TimingRole *TimingRole::non_seq_setup_;
|
||||||
TimingRole *TimingRole::non_seq_hold_;
|
TimingRole *TimingRole::non_seq_hold_;
|
||||||
|
TimingRole *TimingRole::clock_tree_path_min_;
|
||||||
|
TimingRole *TimingRole::clock_tree_path_max_;
|
||||||
|
|
||||||
TimingRoleMap TimingRole::timing_roles_;
|
TimingRoleMap TimingRole::timing_roles_;
|
||||||
|
|
||||||
|
|
@ -111,6 +113,10 @@ TimingRole::init()
|
||||||
MinMax::max(), TimingRole::setup(), 25);
|
MinMax::max(), TimingRole::setup(), 25);
|
||||||
non_seq_hold_ = new TimingRole("non-sequential hold", false, true, true,
|
non_seq_hold_ = new TimingRole("non-sequential hold", false, true, true,
|
||||||
MinMax::min(), TimingRole::hold(), 26);
|
MinMax::min(), TimingRole::hold(), 26);
|
||||||
|
clock_tree_path_min_ = new TimingRole("min clock tree path", false, false, false,
|
||||||
|
MinMax::min(), nullptr, 27);
|
||||||
|
clock_tree_path_max_ = new TimingRole("max clock tree path", false, false, false,
|
||||||
|
MinMax::max(), nullptr, 28);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -358,4 +358,29 @@ ClkSkews::findFanout(Vertex *from)
|
||||||
return endpoints;
|
return endpoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void
|
||||||
|
ClkSkews::findClkDelays(const Clock *clk,
|
||||||
|
// Return values.
|
||||||
|
ClkDelays &delays)
|
||||||
|
{
|
||||||
|
for (Vertex *clk_vertex : *graph_->regClkVertices()) {
|
||||||
|
VertexPathIterator path_iter(clk_vertex, this);
|
||||||
|
while (path_iter.hasNext()) {
|
||||||
|
PathVertex *path = path_iter.next();
|
||||||
|
const ClockEdge *path_clk_edge = path->clkEdge(this);
|
||||||
|
const RiseFall *clk_rf = path_clk_edge->transition();
|
||||||
|
const Clock *path_clk = path_clk_edge->clock();
|
||||||
|
if (path_clk == clk) {
|
||||||
|
Arrival arrival = path->arrival(this);
|
||||||
|
Delay clk_delay = delayAsFloat(arrival) - path_clk_edge->time();
|
||||||
|
const MinMax *min_max = path->minMax(this);
|
||||||
|
const RiseFall *rf = path->transition(this);
|
||||||
|
delays[clk_rf->index()][rf->index()].setValue(min_max, clk_delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
#include "Map.hh"
|
#include "Map.hh"
|
||||||
#include "SdcClass.hh"
|
#include "SdcClass.hh"
|
||||||
#include "StaState.hh"
|
#include "StaState.hh"
|
||||||
|
#include "Transition.hh"
|
||||||
#include "SearchClass.hh"
|
#include "SearchClass.hh"
|
||||||
|
|
||||||
namespace sta {
|
namespace sta {
|
||||||
|
|
@ -27,7 +28,7 @@ class ClkSkew;
|
||||||
|
|
||||||
typedef Map<const Clock*, ClkSkew*> ClkSkewMap;
|
typedef Map<const Clock*, ClkSkew*> ClkSkewMap;
|
||||||
|
|
||||||
// Find and report min clock skews.
|
// Find and report clock skews between source/target registers.
|
||||||
class ClkSkews : public StaState
|
class ClkSkews : public StaState
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -37,9 +38,12 @@ public:
|
||||||
const Corner *corner,
|
const Corner *corner,
|
||||||
const SetupHold *setup_hold,
|
const SetupHold *setup_hold,
|
||||||
int digits);
|
int digits);
|
||||||
// Find worst clock skew.
|
// Find worst clock skew between src/target registers.
|
||||||
float findWorstClkSkew(const Corner *corner,
|
float findWorstClkSkew(const Corner *corner,
|
||||||
const SetupHold *setup_hold);
|
const SetupHold *setup_hold);
|
||||||
|
void findClkDelays(const Clock *clk,
|
||||||
|
// Return values.
|
||||||
|
ClkDelays &delays);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void findClkSkew(ClockSet *clks,
|
void findClkSkew(ClockSet *clks,
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,11 @@
|
||||||
#include "Sta.hh"
|
#include "Sta.hh"
|
||||||
#include "VisitPathEnds.hh"
|
#include "VisitPathEnds.hh"
|
||||||
#include "ArcDelayCalc.hh"
|
#include "ArcDelayCalc.hh"
|
||||||
|
#include "ClkSkew.hh"
|
||||||
|
|
||||||
namespace sta {
|
namespace sta {
|
||||||
|
|
||||||
|
using std::min;
|
||||||
using std::max;
|
using std::max;
|
||||||
using std::make_shared;
|
using std::make_shared;
|
||||||
|
|
||||||
|
|
@ -96,6 +98,7 @@ MakeTimingModel::makeTimingModel()
|
||||||
|
|
||||||
findTimingFromInputs();
|
findTimingFromInputs();
|
||||||
findClkedOutputPaths();
|
findClkedOutputPaths();
|
||||||
|
findClkInsertionDelays();
|
||||||
|
|
||||||
cell_->finish(false, report_, debug_);
|
cell_->finish(false, report_, debug_);
|
||||||
restoreSdc();
|
restoreSdc();
|
||||||
|
|
@ -505,6 +508,63 @@ MakeTimingModel::findClkedOutputPaths()
|
||||||
delete output_iter;
|
delete output_iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void
|
||||||
|
MakeTimingModel::findClkInsertionDelays()
|
||||||
|
{
|
||||||
|
Instance *top_inst = network_->topInstance();
|
||||||
|
Cell *top_cell = network_->cell(top_inst);
|
||||||
|
CellPortIterator *port_iter = network_->portIterator(top_cell);
|
||||||
|
while (port_iter->hasNext()) {
|
||||||
|
Port *port = port_iter->next();
|
||||||
|
if (network_->direction(port)->isInput()) {
|
||||||
|
const char *port_name = network_->name(port);
|
||||||
|
LibertyPort *lib_port = cell_->findLibertyPort(port_name);
|
||||||
|
Pin *pin = network_->findPin(top_inst, port);
|
||||||
|
if (sdc_->isClock(pin)) {
|
||||||
|
lib_port->setIsClock(true);
|
||||||
|
ClockSet *clks = sdc_->findClocks(pin);
|
||||||
|
size_t clk_count = clks->size();
|
||||||
|
if (clk_count == 1) {
|
||||||
|
for (const Clock *clk : *clks) {
|
||||||
|
TimingArcAttrsPtr attrs = nullptr;
|
||||||
|
ClkDelays delays;
|
||||||
|
sta_->findClkDelays(clk, delays);
|
||||||
|
for (const MinMax *min_max : MinMax::range()) {
|
||||||
|
for (const RiseFall *clk_rf : RiseFall::range()) {
|
||||||
|
int clk_rf_index = clk_rf->index();
|
||||||
|
float delay = min_max->initValue();
|
||||||
|
for (const int end_rf_index : RiseFall::rangeIndex()) {
|
||||||
|
float delay1;
|
||||||
|
bool exists;
|
||||||
|
delays[clk_rf_index][end_rf_index].value(min_max, delay1, exists);
|
||||||
|
if (exists)
|
||||||
|
delay = min_max->minMax(delay, delay1);
|
||||||
|
}
|
||||||
|
TimingModel *model = makeGateModelScalar(delay, clk_rf);
|
||||||
|
if (attrs == nullptr)
|
||||||
|
attrs = std::make_shared<TimingArcAttrs>();
|
||||||
|
attrs->setModel(clk_rf, model);
|
||||||
|
}
|
||||||
|
if (attrs)
|
||||||
|
attrs->setTimingSense(TimingSense::positive_unate);
|
||||||
|
TimingRole *role = (min_max == MinMax::min())
|
||||||
|
? TimingRole::clockTreePathMin()
|
||||||
|
: TimingRole::clockTreePathMax();
|
||||||
|
lib_builder_->makeClockTreePathArcs(cell_, lib_port, nullptr,
|
||||||
|
role, attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete port_iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
LibertyPort *
|
LibertyPort *
|
||||||
MakeTimingModel::modelPort(const Pin *pin)
|
MakeTimingModel::modelPort(const Pin *pin)
|
||||||
{
|
{
|
||||||
|
|
@ -514,7 +574,7 @@ MakeTimingModel::modelPort(const Pin *pin)
|
||||||
TimingModel *
|
TimingModel *
|
||||||
MakeTimingModel::makeScalarCheckModel(float value,
|
MakeTimingModel::makeScalarCheckModel(float value,
|
||||||
ScaleFactorType scale_factor_type,
|
ScaleFactorType scale_factor_type,
|
||||||
RiseFall *rf)
|
const RiseFall *rf)
|
||||||
{
|
{
|
||||||
TablePtr table = make_shared<Table0>(value);
|
TablePtr table = make_shared<Table0>(value);
|
||||||
TableTemplate *tbl_template =
|
TableTemplate *tbl_template =
|
||||||
|
|
@ -528,7 +588,7 @@ MakeTimingModel::makeScalarCheckModel(float value,
|
||||||
TimingModel *
|
TimingModel *
|
||||||
MakeTimingModel::makeGateModelScalar(Delay delay,
|
MakeTimingModel::makeGateModelScalar(Delay delay,
|
||||||
Slew slew,
|
Slew slew,
|
||||||
RiseFall *rf)
|
const RiseFall *rf)
|
||||||
{
|
{
|
||||||
TablePtr delay_table = make_shared<Table0>(delayAsFloat(delay));
|
TablePtr delay_table = make_shared<Table0>(delayAsFloat(delay));
|
||||||
TablePtr slew_table = make_shared<Table0>(delayAsFloat(slew));
|
TablePtr slew_table = make_shared<Table0>(delayAsFloat(slew));
|
||||||
|
|
@ -544,12 +604,27 @@ MakeTimingModel::makeGateModelScalar(Delay delay,
|
||||||
return gate_model;
|
return gate_model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimingModel *
|
||||||
|
MakeTimingModel::makeGateModelScalar(Delay delay,
|
||||||
|
const RiseFall *rf)
|
||||||
|
{
|
||||||
|
TablePtr delay_table = make_shared<Table0>(delayAsFloat(delay));
|
||||||
|
TableTemplate *tbl_template =
|
||||||
|
library_->findTableTemplate("scalar", TableTemplateType::delay);
|
||||||
|
TableModel *delay_model = new TableModel(delay_table, tbl_template,
|
||||||
|
ScaleFactorType::cell, rf);
|
||||||
|
GateTableModel *gate_model = new GateTableModel(delay_model, nullptr,
|
||||||
|
nullptr, nullptr,
|
||||||
|
nullptr, nullptr);
|
||||||
|
return gate_model;
|
||||||
|
}
|
||||||
|
|
||||||
// Eval the driver pin model along its load capacitance
|
// Eval the driver pin model along its load capacitance
|
||||||
// axis and add the input to output 'delay' to the table values.
|
// axis and add the input to output 'delay' to the table values.
|
||||||
TimingModel *
|
TimingModel *
|
||||||
MakeTimingModel::makeGateModelTable(const Pin *output_pin,
|
MakeTimingModel::makeGateModelTable(const Pin *output_pin,
|
||||||
Delay delay,
|
Delay delay,
|
||||||
RiseFall *rf)
|
const RiseFall *rf)
|
||||||
{
|
{
|
||||||
const DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_);
|
const DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_);
|
||||||
const Pvt *pvt = dcalc_ap->operatingConditions();
|
const Pvt *pvt = dcalc_ap->operatingConditions();
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ private:
|
||||||
void findTimingFromInputs();
|
void findTimingFromInputs();
|
||||||
void findTimingFromInput(Port *input_port);
|
void findTimingFromInput(Port *input_port);
|
||||||
void findClkedOutputPaths();
|
void findClkedOutputPaths();
|
||||||
|
void findClkInsertionDelays();
|
||||||
void findOutputDelays(const RiseFall *input_rf,
|
void findOutputDelays(const RiseFall *input_rf,
|
||||||
OutputPinDelays &output_pin_delays);
|
OutputPinDelays &output_pin_delays);
|
||||||
void makeSetupHoldTimingArcs(const Pin *input_pin,
|
void makeSetupHoldTimingArcs(const Pin *input_pin,
|
||||||
|
|
@ -70,13 +71,15 @@ private:
|
||||||
OutputPinDelays &output_pin_delays);
|
OutputPinDelays &output_pin_delays);
|
||||||
TimingModel *makeScalarCheckModel(float value,
|
TimingModel *makeScalarCheckModel(float value,
|
||||||
ScaleFactorType scale_factor_type,
|
ScaleFactorType scale_factor_type,
|
||||||
RiseFall *rf);
|
const RiseFall *rf);
|
||||||
TimingModel *makeGateModelScalar(Delay delay,
|
TimingModel *makeGateModelScalar(Delay delay,
|
||||||
Slew slew,
|
Slew slew,
|
||||||
RiseFall *rf);
|
const RiseFall *rf);
|
||||||
|
TimingModel *makeGateModelScalar(Delay delay,
|
||||||
|
const RiseFall *rf);
|
||||||
TimingModel *makeGateModelTable(const Pin *output_pin,
|
TimingModel *makeGateModelTable(const Pin *output_pin,
|
||||||
Delay delay,
|
Delay delay,
|
||||||
RiseFall *rf);
|
const RiseFall *rf);
|
||||||
TableTemplate *ensureTableTemplate(const TableTemplate *drvr_template,
|
TableTemplate *ensureTableTemplate(const TableTemplate *drvr_template,
|
||||||
TableAxisPtr load_axis);
|
TableAxisPtr load_axis);
|
||||||
TableAxisPtr loadCapacitanceAxis(const TableModel *table);
|
TableAxisPtr loadCapacitanceAxis(const TableModel *table);
|
||||||
|
|
|
||||||
|
|
@ -2617,6 +2617,15 @@ Sta::findWorstClkSkew(const SetupHold *setup_hold)
|
||||||
return clk_skews_->findWorstClkSkew(cmd_corner_, setup_hold);
|
return clk_skews_->findWorstClkSkew(cmd_corner_, setup_hold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Sta::findClkDelays(const Clock *clk,
|
||||||
|
// Return values.
|
||||||
|
ClkDelays &delays)
|
||||||
|
{
|
||||||
|
clkSkewPreamble();
|
||||||
|
clk_skews_->findClkDelays(clk, delays);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Sta::clkSkewPreamble()
|
Sta::clkSkewPreamble()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue