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:
James Cherry 2023-10-04 14:33:09 -07:00
parent dad8c74f75
commit 76d6dd1dae
23 changed files with 239 additions and 43 deletions

View File

@ -191,7 +191,7 @@ Graph::makePortInstanceEdges(const Instance *inst,
if ((from_to_port == nullptr
|| from_port == from_to_port
|| to_port == from_to_port)
&& filterEdge(arc_set)) {
&& from_port) {
Pin *from_pin = network_->findPin(inst, from_port);
Pin *to_pin = network_->findPin(inst, to_port);
if (from_pin && to_pin) {

View File

@ -232,8 +232,6 @@ protected:
Edge *edge);
void removeDelays();
void removeDelayAnnotated(Edge *edge);
// User defined predicate to filter graph edges for liberty timing arcs.
virtual bool filterEdge(TimingArcSet *) const { return true; }
VertexTable *vertices_;
EdgeTable *edges_;

View File

@ -110,8 +110,10 @@ public:
bool exists = exists_[mm_index];
if (exists)
return values_[mm_index];
else
else {
criticalError(226, "uninitialized value reference");
return 0.0;
}
}
void

View File

@ -23,6 +23,7 @@
#include "Map.hh"
#include "UnorderedMap.hh"
#include "StringSet.hh"
#include "MinMaxValues.hh"
#include "Delay.hh"
#include "NetworkClass.hh"
#include "GraphClass.hh"
@ -113,6 +114,7 @@ typedef Vector<PathVertex> PathVertexSeq;
typedef Vector<Slack> SlackSeq;
typedef Delay Crpr;
typedef Vector<PathRef> PathRefSeq;
typedef MinMaxValues<float> ClkDelays[RiseFall::index_count][RiseFall::index_count];
enum class ReportPathFormat { full,
full_clock,

View File

@ -900,12 +900,6 @@ public:
void setReportPathDigits(int digits);
void setReportPathNoSplit(bool no_split);
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.
void reportPathEndHeader();
// Footer below reportPathEnd results.
@ -919,6 +913,18 @@ public:
void reportPathEnds(PathEndSeq *ends);
ReportPath *reportPath() { return report_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.
// If necessary updateTiming propagates arrivals around latch
// loops until the arrivals converge.

View File

@ -201,7 +201,7 @@ public:
TableModel(TablePtr table,
TableTemplate *tbl_template,
ScaleFactorType scale_factor_type,
RiseFall *rf);
const RiseFall *rf);
void setScaleFactorType(ScaleFactorType type);
int order() const;
TableTemplate *tblTemplate() const { return tbl_template_; }

View File

@ -105,8 +105,8 @@ public:
void setModeName(const char *name);
const char *modeValue() const { return mode_value_; }
void setModeValue(const char *value);
TimingModel *model(RiseFall *rf) const;
void setModel(RiseFall *rf,
TimingModel *model(const RiseFall *rf) const;
void setModel(const RiseFall *rf,
TimingModel *model);
float ocvArcDepth() const { return ocv_arc_depth_; }
void setOcvArcDepth(float depth);

View File

@ -59,6 +59,8 @@ public:
static TimingRole *dataCheckHold() { return data_check_hold_; }
static TimingRole *nonSeqSetup() { return non_seq_setup_; }
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_; }
int index() const { return index_; }
bool isWire() const;
@ -125,6 +127,8 @@ private:
static TimingRole *data_check_hold_;
static TimingRole *non_seq_setup_;
static TimingRole *non_seq_hold_;
static TimingRole *clock_tree_path_min_;
static TimingRole *clock_tree_path_max_;
static TimingRoleMap timing_roles_;
friend class TimingRoleLess;

View File

@ -55,9 +55,9 @@ public:
RiseFall *opposite() const;
// for range support.
// for (auto tr : RiseFall::range()) {}
// for (auto rf : RiseFall::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 int index_count = 2;
static const int index_max = (index_count - 1);

View File

@ -2352,15 +2352,16 @@ bool
LibertyPort::less(const LibertyPort *port1,
const LibertyPort *port2)
{
if (port1 == nullptr && port2 != nullptr)
return true;
if (port1 != nullptr && port2 == nullptr)
return false;
const char *name1 = port1->name();
const char *name2 = port2->name();
if (stringEq(name1, name2)) {
PortDirection *dir1 = port1->direction();
PortDirection *dir2 = port2->direction();
if (dir1 == dir2) {
}
else
return dir1->index() < dir2->index();
return dir1->index() < dir2->index();
}
return stringLess(name1, name2);
}
@ -2588,8 +2589,8 @@ bool
LibertyPortPairLess::operator()(const LibertyPortPair &pair1,
const LibertyPortPair &pair2) const
{
ObjectId id1 = pair1.first->id();
ObjectId id2 = pair2.first->id();
ObjectId id1 = pair1.first ? pair1.first->id() : 0;
ObjectId id2 = pair2.first ? pair2.first->id() : 0;
return id1 < id2
|| (id1 == id2
&& pair1.second->id() < pair2.second->id());

View File

@ -281,6 +281,14 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell,
RiseFall::fall(),
TimingRole::nonSeqHold(),
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::minimum_period:
case TimingType::nochange_high_high:
@ -289,8 +297,6 @@ LibertyBuilder::makeTimingArcs(LibertyCell *cell,
case TimingType::nochange_low_low:
case TimingType::retaining_time:
case TimingType::unknown:
case TimingType::min_clock_tree_path:
case TimingType::max_clock_tree_path:
return nullptr;
}
// Prevent warnings from lame compilers.
@ -647,6 +653,23 @@ LibertyBuilder::makeTristateDisableArcs(LibertyCell *cell,
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 *
LibertyBuilder::makeTimingArcSet(LibertyCell *cell,
LibertyPort *from,

View File

@ -79,6 +79,11 @@ public:
bool to_rise,
bool to_fall,
TimingArcAttrsPtr attrs);
TimingArcSet *makeClockTreePathArcs(LibertyCell *cell,
LibertyPort *to_port,
LibertyPort *related_out,
TimingRole *role,
TimingArcAttrsPtr attrs);
protected:
ConcretePort *makeBusPort(const char *name,

View File

@ -2195,6 +2195,8 @@ LibertyReader::makeTimingArcs(LibertyPort *to_port,
}
}
}
else
makeTimingArcs(to_port, related_out_port, timing);
}
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

View File

@ -178,6 +178,9 @@ public:
LibertyPort *to_port,
LibertyPort *related_out_port,
TimingGroup *timing);
virtual void makeTimingArcs(LibertyPort *to_port,
LibertyPort *related_out_port,
TimingGroup *timing);
virtual void visitClockGatingIntegratedCell(LibertyAttr *attr);
virtual void visitArea(LibertyAttr *attr);

View File

@ -364,7 +364,8 @@ void
LibertyWriter::writeTimingArcSet(const TimingArcSet *arc_set)
{
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();
if (sense != TimingSense::unknown
&& sense != TimingSense::non_unate)
@ -398,11 +399,14 @@ LibertyWriter::writeTimingModels(const TimingArc *arc,
fprintf(stream_, " }\n");
const TableModel *slew_model = gate_model->slewModel();
template_name = slew_model->tblTemplate()->name();
fprintf(stream_, " %s_transition(%s) {\n", rf->name(), template_name);
writeTableModel(slew_model);
fprintf(stream_, " }\n");
} else if (check_model) {
if (slew_model) {
template_name = slew_model->tblTemplate()->name();
fprintf(stream_, " %s_transition(%s) {\n", rf->name(), template_name);
writeTableModel(slew_model);
fprintf(stream_, " }\n");
}
}
else if (check_model) {
const TableModel *model = check_model->model();
const char *template_name = model->tblTemplate()->name();
fprintf(stream_, " %s_constraint(%s) {\n", rf->name(), template_name);
@ -569,11 +573,15 @@ LibertyWriter::timingTypeString(const TimingArcSet *arc_set)
else
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 {
report_->error(703, "%s/%s/%s timing arc type %s not supported.",
library_->name(),
arc_set->from()->libertyCell()->name(),
arc_set->from()->name(),
arc_set->to()->libertyCell()->name(),
arc_set->to()->name(),
role->asString());
return nullptr;
}

View File

@ -611,7 +611,7 @@ CheckTableModel::checkAxis(TableAxisPtr axis)
TableModel::TableModel(TablePtr table,
TableTemplate *tbl_template,
ScaleFactorType scale_factor_type,
RiseFall *rf) :
const RiseFall *rf) :
table_(table),
tbl_template_(tbl_template),
scale_factor_type_(int(scale_factor_type)),

View File

@ -127,13 +127,13 @@ TimingArcAttrs::setModeValue(const char *value)
}
TimingModel *
TimingArcAttrs::model(RiseFall *rf) const
TimingArcAttrs::model(const RiseFall *rf) const
{
return models_[rf->index()];
}
void
TimingArcAttrs::setModel(RiseFall *rf,
TimingArcAttrs::setModel(const RiseFall *rf,
TimingModel *model)
{
models_[rf->index()] = model;

View File

@ -45,6 +45,8 @@ TimingRole *TimingRole::data_check_setup_;
TimingRole *TimingRole::data_check_hold_;
TimingRole *TimingRole::non_seq_setup_;
TimingRole *TimingRole::non_seq_hold_;
TimingRole *TimingRole::clock_tree_path_min_;
TimingRole *TimingRole::clock_tree_path_max_;
TimingRoleMap TimingRole::timing_roles_;
@ -111,6 +113,10 @@ TimingRole::init()
MinMax::max(), TimingRole::setup(), 25);
non_seq_hold_ = new TimingRole("non-sequential hold", false, true, true,
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

View File

@ -358,4 +358,29 @@ ClkSkews::findFanout(Vertex *from)
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

View File

@ -19,6 +19,7 @@
#include "Map.hh"
#include "SdcClass.hh"
#include "StaState.hh"
#include "Transition.hh"
#include "SearchClass.hh"
namespace sta {
@ -27,7 +28,7 @@ class ClkSkew;
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
{
public:
@ -37,9 +38,12 @@ public:
const Corner *corner,
const SetupHold *setup_hold,
int digits);
// Find worst clock skew.
// Find worst clock skew between src/target registers.
float findWorstClkSkew(const Corner *corner,
const SetupHold *setup_hold);
void findClkDelays(const Clock *clk,
// Return values.
ClkDelays &delays);
protected:
void findClkSkew(ClockSet *clks,

View File

@ -40,9 +40,11 @@
#include "Sta.hh"
#include "VisitPathEnds.hh"
#include "ArcDelayCalc.hh"
#include "ClkSkew.hh"
namespace sta {
using std::min;
using std::max;
using std::make_shared;
@ -96,6 +98,7 @@ MakeTimingModel::makeTimingModel()
findTimingFromInputs();
findClkedOutputPaths();
findClkInsertionDelays();
cell_->finish(false, report_, debug_);
restoreSdc();
@ -505,6 +508,63 @@ MakeTimingModel::findClkedOutputPaths()
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 *
MakeTimingModel::modelPort(const Pin *pin)
{
@ -514,7 +574,7 @@ MakeTimingModel::modelPort(const Pin *pin)
TimingModel *
MakeTimingModel::makeScalarCheckModel(float value,
ScaleFactorType scale_factor_type,
RiseFall *rf)
const RiseFall *rf)
{
TablePtr table = make_shared<Table0>(value);
TableTemplate *tbl_template =
@ -528,7 +588,7 @@ MakeTimingModel::makeScalarCheckModel(float value,
TimingModel *
MakeTimingModel::makeGateModelScalar(Delay delay,
Slew slew,
RiseFall *rf)
const RiseFall *rf)
{
TablePtr delay_table = make_shared<Table0>(delayAsFloat(delay));
TablePtr slew_table = make_shared<Table0>(delayAsFloat(slew));
@ -544,12 +604,27 @@ MakeTimingModel::makeGateModelScalar(Delay delay,
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
// axis and add the input to output 'delay' to the table values.
TimingModel *
MakeTimingModel::makeGateModelTable(const Pin *output_pin,
Delay delay,
RiseFall *rf)
const RiseFall *rf)
{
const DcalcAnalysisPt *dcalc_ap = corner_->findDcalcAnalysisPt(min_max_);
const Pvt *pvt = dcalc_ap->operatingConditions();

View File

@ -62,6 +62,7 @@ private:
void findTimingFromInputs();
void findTimingFromInput(Port *input_port);
void findClkedOutputPaths();
void findClkInsertionDelays();
void findOutputDelays(const RiseFall *input_rf,
OutputPinDelays &output_pin_delays);
void makeSetupHoldTimingArcs(const Pin *input_pin,
@ -70,13 +71,15 @@ private:
OutputPinDelays &output_pin_delays);
TimingModel *makeScalarCheckModel(float value,
ScaleFactorType scale_factor_type,
RiseFall *rf);
const RiseFall *rf);
TimingModel *makeGateModelScalar(Delay delay,
Slew slew,
RiseFall *rf);
const RiseFall *rf);
TimingModel *makeGateModelScalar(Delay delay,
const RiseFall *rf);
TimingModel *makeGateModelTable(const Pin *output_pin,
Delay delay,
RiseFall *rf);
const RiseFall *rf);
TableTemplate *ensureTableTemplate(const TableTemplate *drvr_template,
TableAxisPtr load_axis);
TableAxisPtr loadCapacitanceAxis(const TableModel *table);

View File

@ -2617,6 +2617,15 @@ Sta::findWorstClkSkew(const SetupHold *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
Sta::clkSkewPreamble()
{