write_timing_model setup/hold search

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2022-06-10 09:39:49 -07:00
parent 170e6b7a40
commit 706660a503
5 changed files with 193 additions and 80 deletions

Binary file not shown.

View File

@ -348,6 +348,10 @@ public:
GatedClk *gatedClk() { return gated_clk_; }
Genclks *genclks() { return genclks_; }
void findClkVertexPins(PinSet &clk_pins);
void findFilteredArrivals(ExceptionFrom *from,
ExceptionThruSeq *thrus,
ExceptionTo *to,
bool unconstrained);
protected:
void init(StaState *sta);

View File

@ -16,6 +16,8 @@
#include "MakeTimingModel.hh"
#include <map>
#include "Debug.hh"
#include "Units.hh"
#include "Transition.hh"
@ -30,8 +32,11 @@
#include "dcalc/GraphDelayCalc1.hh"
#include "Sdc.hh"
#include "StaState.hh"
#include "Graph.hh"
#include "PathEnd.hh"
#include "Search.hh"
#include "Sta.hh"
#include "VisitPathEnds.hh"
namespace sta {
@ -61,8 +66,12 @@ MakeTimingModel::makeTimingModel(const char *cell_name,
for (Clock *clk : *sdc_->clocks())
sta_->setPropagatedClock(clk);
#if 0
//findInputToOutputPaths();
findInputSetupHolds();
#endif
sta_->searchPreamble();
findInputSetupHolds();
findClkedOutputPaths();
cell_->finish(false, report_, debug_);
@ -142,7 +151,10 @@ MakeTimingModel::makePorts()
delete port_iter;
}
////////////////////////////////////////////////////////////////
// input -> output combinational paths
// too slow to use
void
MakeTimingModel::findInputToOutputPaths()
{
@ -183,75 +195,150 @@ MakeTimingModel::findInputToOutputPaths()
}
}
////////////////////////////////////////////////////////////////
typedef std::map<ClockEdge*, RiseFallMinMax> ClockMargins;
class MakeEndTimingArcs : public PathEndVisitor
{
public:
MakeEndTimingArcs(Sta *sta);
MakeEndTimingArcs(const MakeEndTimingArcs&) = default;
virtual ~MakeEndTimingArcs() {}
virtual PathEndVisitor *copy() const;
virtual void visit(PathEnd *path_end);
void setInputPin(const Pin *input_pin);
void setInputRf(const RiseFall *input_rf);
const ClockMargins &margins() const { return margins_; }
private:
Sta *sta_;
const Pin *input_pin_;
const RiseFall *input_rf_;
ClockMargins margins_;
};
MakeEndTimingArcs::MakeEndTimingArcs(Sta *sta) :
sta_(sta)
{
}
PathEndVisitor *
MakeEndTimingArcs::copy() const
{
return new MakeEndTimingArcs(*this);
}
void
MakeEndTimingArcs::setInputPin(const Pin *input_pin)
{
input_pin_ = input_pin;
margins_.clear();
}
void
MakeEndTimingArcs::setInputRf(const RiseFall *input_rf)
{
input_rf_ = input_rf;
}
void
MakeEndTimingArcs::visit(PathEnd *path_end)
{
ClockEdge *tgt_clk_edge = path_end->targetClkEdge(sta_);
Debug *debug = sta_->debug();
const MinMax *min_max = path_end->minMax(sta_);
debugPrint(debug, "make_timing_model", 2, "%s %s %s %s -> clock %s",
path_end->typeName(),
min_max->asString(),
sta_->network()->pathName(input_pin_),
input_rf_->shortName(),
tgt_clk_edge->name());
if (debug->check("make_timing_model", 3))
sta_->reportPathEnd(path_end);
Arrival data_arrival = path_end->path()->arrival(sta_);
Delay clk_latency = path_end->targetClkDelay(sta_);
ArcDelay check_setup = path_end->margin(sta_);
float margin = data_arrival - clk_latency + check_setup;
RiseFallMinMax &margins = margins_[tgt_clk_edge];
margins.setValue(input_rf_, min_max, margin);
}
// input -> register setup/hold
// Use default input arrival (set_input_delay with no clock) from inputs
// to find downstream register checks and output ports.
void
MakeTimingModel::findInputSetupHolds()
{
Debug *debug = sta_->debug();
VisitPathEnds visit_ends(sta_);
MakeEndTimingArcs end_visitor(sta_);
InstancePinIterator *input_iter = network_->pinIterator(network_->topInstance());
while (input_iter->hasNext()) {
Pin *input_pin = input_iter->next();
if (network_->direction(input_pin)->isInput()
&& !sta_->isClockSrc(input_pin)) {
LibertyPort *input_port = modelPort(input_pin);
for (Clock *clk : *sdc_->clocks()) {
for (const Pin *clk_pin : clk->pins()) {
LibertyPort *clk_port = modelPort(clk_pin);
for (RiseFall *clk_rf : RiseFall::range()) {
for (MinMax *min_max : MinMax::range()) {
MinMaxAll *min_max1 = min_max->asMinMaxAll();
bool setup = min_max == MinMax::max();
bool hold = !setup;
end_visitor.setInputPin(input_pin);
for (RiseFall *input_rf : RiseFall::range()) {
RiseFallBoth *input_rf1 = input_rf->asRiseFallBoth();
sta_->setInputDelay(input_pin, input_rf1,
sdc_->defaultArrivalClock(),
sdc_->defaultArrivalClockEdge()->transition(),
nullptr, false, false, MinMaxAll::all(), false, 0.0);
PinSet *from_pins = new PinSet;
from_pins->insert(input_pin);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
input_rf1);
search_->deleteFilteredArrivals();
search_->findFilteredArrivals(from, nullptr, nullptr, false);
end_visitor.setInputRf(input_rf);
for (Vertex *end : *search_->endpoints())
visit_ends.visitPathEnds(end, corner_, MinMaxAll::all(), true, &end_visitor);
sta_->removeInputDelay(input_pin, input_rf1,
sdc_->defaultArrivalClock(),
sdc_->defaultArrivalClockEdge()->transition(),
MinMaxAll::all());
}
const ClockMargins &clk_margins = end_visitor.margins();
for (auto clk_edge_margins : clk_margins) {
ClockEdge *clk_edge = clk_edge_margins.first;
RiseFallMinMax &margins = clk_edge_margins.second;
for (MinMax *min_max : MinMax::range()) {
bool setup = (min_max == MinMax::max());
TimingArcAttrs *attrs = nullptr;
for (RiseFall *input_rf : RiseFall::range()) {
float margin;
bool exists;
margins.value(input_rf, min_max, margin, exists);
if (exists) {
debugPrint(debug, "make_timing_model", 1, "%s %s %s -> clock %s %s",
sta_->network()->pathName(input_pin),
input_rf->shortName(),
min_max == MinMax::max() ? "setup" : "hold",
clk_edge->name(),
delayAsString(margin, sta_));
ScaleFactorType scale_type = setup
? ScaleFactorType::setup
: ScaleFactorType::hold;
TimingModel *check_model = makeScalarCheckModel(margin, scale_type, input_rf);
if (attrs == nullptr)
attrs = new TimingArcAttrs();
attrs->setModel(input_rf, check_model);
}
}
if (attrs) {
LibertyPort *input_port = modelPort(input_pin);
for (const Pin *clk_pin : clk_edge->clock()->pins()) {
LibertyPort *clk_port = modelPort(clk_pin);
RiseFall *clk_rf = clk_edge->transition();
TimingRole *role = setup ? TimingRole::setup() : TimingRole::hold();
TimingArcAttrs *attrs = nullptr;
for (RiseFall *input_rf : RiseFall::range()) {
RiseFallBoth *input_rf1 = input_rf->asRiseFallBoth();
sta_->setInputDelay(input_pin, input_rf1, clk, clk_rf,
nullptr, false, false, min_max1, false, 0.0);
PinSet *from_pins = new PinSet;
from_pins->insert(input_pin);
ExceptionFrom *from = sta_->makeExceptionFrom(from_pins, nullptr, nullptr,
input_rf1);
ClockSet *to_clks = new ClockSet;
to_clks->insert(clk);
ExceptionTo *to = sta_->makeExceptionTo(nullptr, to_clks, nullptr,
clk_rf->asRiseFallBoth(),
RiseFallBoth::riseFall());
PathEndSeq *ends = sta_->findPathEnds(from, nullptr, to, false, corner_,
min_max1,
1, 1, false,
-INF, INF, false, nullptr,
setup, hold, setup, hold, setup, hold);
if (!ends->empty()) {
PathEnd *end = (*ends)[0];
debugPrint(debug_, "make_timing_model", 1, "%s %s %s -> clock %s %s",
setup ? "setup" : "hold",
network_->pathName(input_pin),
input_rf->asString(),
clk->name(),
clk_rf->asString());
if (debug_->check("make_timing_model", 2))
sta_->reportPathEnd(end);
Arrival data_arrival = end->path()->arrival(sta_);
Delay clk_latency = end->targetClkDelay(sta_);
ArcDelay check_setup = end->margin(sta_);
float margin = data_arrival - clk_latency + check_setup;
ScaleFactorType scale_type = setup
? ScaleFactorType::setup
: ScaleFactorType::hold;
TimingModel *check_model = makeScalarCheckModel(margin, scale_type, input_rf);
if (attrs == nullptr)
attrs = new TimingArcAttrs();
attrs->setModel(input_rf, check_model);
}
sta_->removeInputDelay(input_pin, input_rf1, clk, clk_rf, min_max1);
}
if (attrs)
lib_builder_->makeFromTransitionArcs(cell_, clk_port,
input_port, nullptr,
clk_rf, role, attrs);
lib_builder_->makeFromTransitionArcs(cell_, clk_port,
input_port, nullptr,
clk_rf, role, attrs);
}
}
}
@ -260,6 +347,9 @@ MakeTimingModel::findInputSetupHolds()
}
}
////////////////////////////////////////////////////////////////
// Rewrite to use non-filtered arrivals at outputs from each clock.
void
MakeTimingModel::findClkedOutputPaths()
{

View File

@ -442,6 +442,30 @@ Search::findPathEnds(ExceptionFrom *from,
bool removal,
bool clk_gating_setup,
bool clk_gating_hold)
{
findFilteredArrivals(from, thrus, to, unconstrained);
if (!sdc_->recoveryRemovalChecksEnabled())
recovery = removal = false;
if (!sdc_->gatedClkChecksEnabled())
clk_gating_setup = clk_gating_hold = false;
path_groups_ = makePathGroups(group_count, endpoint_count, unique_pins,
slack_min, slack_max,
group_names, setup, hold,
recovery, removal,
clk_gating_setup, clk_gating_hold);
ensureDownstreamClkPins();
PathEndSeq *path_ends = path_groups_->makePathEnds(to, unconstrained_paths_,
corner, min_max,
sort_by_slack);
sdc_->reportClkToClkMaxCycleWarnings();
return path_ends;
}
void
Search::findFilteredArrivals(ExceptionFrom *from,
ExceptionThruSeq *thrus,
ExceptionTo *to,
bool unconstrained)
{
unconstrained_paths_ = unconstrained;
// Delete results from last findPathEnds.
@ -462,21 +486,6 @@ Search::findPathEnds(ExceptionFrom *from,
// -from clocks
// -to
findAllArrivals();
if (!sdc_->recoveryRemovalChecksEnabled())
recovery = removal = false;
if (!sdc_->gatedClkChecksEnabled())
clk_gating_setup = clk_gating_hold = false;
path_groups_ = makePathGroups(group_count, endpoint_count, unique_pins,
slack_min, slack_max,
group_names, setup, hold,
recovery, removal,
clk_gating_setup, clk_gating_hold);
ensureDownstreamClkPins();
PathEndSeq *path_ends = path_groups_->makePathEnds(to, unconstrained_paths_,
corner, min_max,
sort_by_slack);
sdc_->reportClkToClkMaxCycleWarnings();
return path_ends;
}
// From/thrus/to are used to make a filter exception. If the last
@ -3981,7 +3990,10 @@ Search::deletePathGroups()
PathGroup *
Search::pathGroup(const PathEnd *path_end) const
{
return path_groups_->pathGroup(path_end);
if (path_groups_)
return path_groups_->pathGroup(path_end);
else
return nullptr;
}
bool

View File

@ -1032,14 +1032,21 @@ proc worst_clock_skew { args } {
################################################################
define_cmd_args "write_timing_model" {[-corner corner] cell_name filename}
define_cmd_args "write_timing_model" {[-corner corner] \
[-cell_name cell_name]\
filename}
proc write_timing_model { args } {
parse_key_args "write_timing_model" args keys {corner} flags {}
check_argc_eq2 "write_timing_model" $args
parse_key_args "write_timing_model" args \
keys {-cell_name -corner} flags {}
check_argc_eq1 "write_timing_model" $args
set cell_name [lindex $args 0]
set filename [lindex $args 1]
set filename [lindex $args 0]
if { [info exists keys(-cell_name)] } {
set cell_name $keys(-cell_name)
} else {
set cell_name [get_name [[top_instance] cell]]
}
set corner [parse_corner keys]
write_timing_model_cmd $cell_name [file nativename $filename] $corner
}