find_timing_paths

This commit is contained in:
James Cherry 2018-12-20 22:41:54 -08:00
parent d29d66c7fb
commit e1059eac12
16 changed files with 353 additions and 152 deletions

2
README
View File

@ -15,7 +15,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
Parallax Gate Level Static Timing Analyzer
See INSTALL for build instructions.
See INSTALL for installation and build instructions.
Standard file formats
Verilog

View File

@ -135,6 +135,14 @@ LibertyLibrary::~LibertyLibrary()
deleteEquivCellMap(equiv_cell_map_);
delete units_;
ocv_derate_map_.deleteContents();
SupplyVoltageMap::Iterator supply_iter(supply_voltage_map_);
while (supply_iter.hasNext()) {
const char *supply_name;
float voltage;
supply_iter.next(supply_name, voltage);
stringDelete(supply_name);
}
}
LibertyCell *
@ -762,6 +770,19 @@ LibertyLibrary::addOcvDerate(OcvDerate *derate)
ocv_derate_map_[derate->name()] = derate;
}
void
LibertyLibrary::addSupplyVoltage(const char *supply_name,
float voltage)
{
supply_voltage_map_[stringCopy(supply_name)] = voltage;
}
float
LibertyLibrary::supplyVoltage(const char *supply_name)
{
return supply_voltage_map_[supply_name];
}
////////////////////////////////////////////////////////////////
LibertyCellIterator::LibertyCellIterator(const LibertyLibrary *

View File

@ -68,6 +68,7 @@ typedef Map<TimingArcSet*, LatchEnable*> LatchEnableMap;
typedef Map<const char *, OcvDerate*, CharPtrLess> OcvDerateMap;
typedef Vector<TimingArcAttrs*> TimingArcAttrsSeq;
typedef Vector<InternalPowerAttrs*> InternalPowerAttrsSeq;
typedef Map<const char *, float, CharPtrLess> SupplyVoltageMap;
typedef enum {
clock_gate_none,
@ -263,6 +264,9 @@ public:
void setDefaultOcvDerate(OcvDerate *derate);
OcvDerate *findOcvDerate(const char *derate_name);
void addOcvDerate(OcvDerate *derate);
void addSupplyVoltage(const char *suppy_name,
float voltage);
float supplyVoltage(const char *suppy_name);
// Make scaled cell. Call LibertyCell::addScaledCell after it is complete.
LibertyCell *makeScaledCell(const char *name,
@ -323,6 +327,7 @@ protected:
float ocv_arc_depth_;
OcvDerate *default_ocv_derate_;
OcvDerateMap ocv_derate_map_;
SupplyVoltageMap supply_voltage_map_;
// Set if any library has rise/fall capacitances.
static bool found_rise_fall_caps_;

View File

@ -178,6 +178,7 @@ LibertyReader::defineVisitors()
defineAttrVisitor("leakage_power_unit", &LibertyReader::visitPowerUnit);
defineAttrVisitor("delay_model", &LibertyReader::visitDelayModel);
defineAttrVisitor("bus_naming_style", &LibertyReader::visitBusStyle);
defineAttrVisitor("voltage_map", &LibertyReader::visitVoltageMap);
defineAttrVisitor("nom_temperature", &LibertyReader::visitNomTemp);
defineAttrVisitor("nom_voltage", &LibertyReader::visitNomVolt);
defineAttrVisitor("nom_process", &LibertyReader::visitNomProc);
@ -832,6 +833,39 @@ LibertyReader::visitBusStyle(LibertyAttr *attr)
}
}
void
LibertyReader::visitVoltageMap(LibertyAttr *attr)
{
if (library_) {
if (attr->isComplex()) {
LibertyAttrValueIterator value_iter(attr->values());
if (value_iter.hasNext()) {
LibertyAttrValue *value = value_iter.next();
if (value->isString()) {
const char *supply_name = value->stringValue();
if (value_iter.hasNext()) {
value = value_iter.next();
if (value->isFloat()) {
float voltage = value->floatValue();
library_->addSupplyVoltage(supply_name, voltage);
}
else
libWarn(attr, "voltage_map voltage is not a float.\n");
}
else
libWarn(attr, "voltage_map missing voltage.\n");
}
else
libWarn(attr, "voltage_map supply name is not a string.\n");
}
else
libWarn(attr, "voltage_map missing supply name and voltage.\n");
}
else
libWarn(attr, "voltage_map missing values suffix.\n");
}
}
void
LibertyReader::visitNomTemp(LibertyAttr *attr)
{

View File

@ -88,6 +88,7 @@ public:
float &scale_var,
Unit *unit_suffix);
virtual void visitDelayModel(LibertyAttr *attr);
virtual void visitVoltageMap(LibertyAttr *attr);
virtual void visitBusStyle(LibertyAttr *attr);
virtual void visitNomTemp(LibertyAttr *attr);
virtual void visitNomVolt(LibertyAttr *attr);

View File

@ -21,6 +21,7 @@
#include "Clock.hh"
#include "Tag.hh"
#include "Corner.hh"
#include "DcalcAnalysisPt.hh"
#include "PathAnalysisPt.hh"
#include "PathRef.hh"
#include "Path.hh"
@ -97,6 +98,19 @@ Path::pathAnalysisPtIndex(const StaState *sta) const
return pathAnalysisPt(sta)->index();
}
DcalcAnalysisPt *
Path::dcalcAnalysisPt(const StaState *sta) const
{
return pathAnalysisPt(sta)->dcalcAnalysisPt();
}
Slew
Path::slew(const StaState *sta) const
{
return sta->graph()->slew(vertex(sta), transition(sta),
dcalcAnalysisPt(sta)->index());
}
int
Path::trIndex(const StaState *sta) const
{

View File

@ -29,6 +29,8 @@
namespace sta {
class DcalcAnalysisPt;
// Abstract base class for Path API.
class Path
{
@ -55,6 +57,7 @@ public:
virtual const MinMax *minMax(const StaState *sta) const;
virtual PathAnalysisPt *pathAnalysisPt(const StaState *sta) const = 0;
virtual PathAPIndex pathAnalysisPtIndex(const StaState *sta) const;
virtual DcalcAnalysisPt *dcalcAnalysisPt(const StaState *sta) const;
virtual Arrival arrival(const StaState *sta) const = 0;
virtual void setArrival(Arrival arrival,
const StaState *sta) = 0;
@ -66,6 +69,7 @@ public:
virtual void initRequired(const StaState *sta);
virtual bool requiredIsInitValue(const StaState *sta) const;
virtual Slack slack(const StaState *sta) const;
virtual Slew slew(const StaState *sta) const;
// This takes the same time as prevPath and prevArc combined.
virtual void prevPath(const StaState *sta,
// Return values.

View File

@ -256,10 +256,63 @@ ReportPath::setDigits(int digits)
////////////////////////////////////////////////////////////////
void
ReportPath::reportPathEndHeader()
{
string header;
switch (format_) {
case report_path_full:
case report_path_full_clock:
case report_path_full_clock_expanded:
case report_path_short:
case report_path_endpoint:
break;
case report_path_summary:
reportSummaryHeader(header);
report_->print(header);
break;
case report_path_slack_only:
reportSlackOnlyHeader(header);
report_->print(header);
break;
default:
internalError("unsupported path type");
break;
}
}
void
ReportPath::reportPathEndFooter()
{
string header;
switch (format_) {
case report_path_full:
case report_path_full_clock:
case report_path_full_clock_expanded:
case report_path_short:
break;
case report_path_endpoint:
case report_path_summary:
case report_path_slack_only:
report_->print("\n");
break;
default:
internalError("unsupported path type");
break;
}
}
void
ReportPath::reportPathEnd(PathEnd *end)
{
string header, result;
reportPathEnd(end, NULL);
}
void
ReportPath::reportPathEnd(PathEnd *end,
PathEnd *prev_end)
{
string result;
switch (format_) {
case report_path_full:
case report_path_full_clock:
@ -273,24 +326,18 @@ ReportPath::reportPathEnd(PathEnd *end)
report_->print(result);
report_->print("\n\n");
break;
case report_path_end:
reportEndHeader(header);
report_->print(header);
case report_path_endpoint:
reportEndpointHeader(end, prev_end);
reportEndLine(end, result);
report_->print(result);
break;
case report_path_summary:
reportSummaryHeader(header);
report_->print(header);
reportSummaryLine(end, result);
report_->print(result);
break;
case report_path_slack_only:
reportSlackOnlyHeader(header);
report_->print(header);
reportSlackOnly(end, result);
report_->print(result);
report_->print("\n");
break;
default:
internalError("unsupported path type");
@ -301,89 +348,42 @@ ReportPath::reportPathEnd(PathEnd *end)
void
ReportPath::reportPathEnds(PathEndSeq *ends)
{
if (!ends->empty()) {
string header;
PathEndSeq::Iterator end_iter(ends);
switch (format_) {
case report_path_full:
case report_path_full_clock:
case report_path_full_clock_expanded:
while (end_iter.hasNext()) {
PathEnd *end = end_iter.next();
string result;
end->reportFull(this, result);
report_->print(result);
report_->print("\n\n");
}
break;
case report_path_short:
while (end_iter.hasNext()) {
PathEnd *end = end_iter.next();
string result;
end->reportShort(this, result);
report_->print(result);
report_->print("\n\n");
}
break;
case report_path_end:
reportPathEndsEnd(ends);
break;
case report_path_summary:
reportSummaryHeader(header);
report_->print(header);
while (end_iter.hasNext()) {
PathEnd *end = end_iter.next();
string result;
reportSummaryLine(end, result);
report_->print(result);
}
report_->print("\n");
break;
case report_path_slack_only:
reportSlackOnlyHeader(header);
report_->print(header);
while (end_iter.hasNext()) {
PathEnd *end = end_iter.next();
string result;
reportSlackOnly(end, result);
report_->print(result);
}
report_->print("\n");
break;
default:
internalError("unsupported path type");
break;
}
reportPathEndHeader();
PathEndSeq::Iterator end_iter(ends);
PathEnd *prev_end = NULL;
while (end_iter.hasNext()) {
PathEnd *end = end_iter.next();
reportEndpointHeader(end, prev_end);
string result;
end->reportFull(this, result);
report_->print(result);
report_->print("\n\n");
prev_end = end;
}
reportPathEndFooter();
}
void
ReportPath::reportPathEndsEnd(PathEndSeq *ends)
ReportPath::reportEndpointHeader(PathEnd *end,
PathEnd *prev_end)
{
PathGroup *prev_group = NULL;
PathEndSeq::Iterator end_iter(ends);
while (end_iter.hasNext()) {
PathEnd *end = end_iter.next();
PathGroup *group = search_->pathGroup(end);
if (group != prev_group) {
if (prev_group)
report_->print("\n");
const char *setup_hold = (end->minMax(this) == MinMax::min())
? "min_delay/hold"
: "max_delay/setup";
report_->print("%s ('%s' group)\n\n",
setup_hold,
group->name());
string header;
reportEndHeader(header);
report_->print(header);
}
string result;
reportEndLine(end, result);
report_->print(result);
prev_group = group;
if (prev_end)
prev_group = search_->pathGroup(prev_end);
PathGroup *group = search_->pathGroup(end);
if (group != prev_group) {
if (prev_group)
report_->print("\n");
const char *setup_hold = (end->minMax(this) == MinMax::min())
? "min_delay/hold"
: "max_delay/setup";
report_->print("%s ('%s' group)\n\n",
setup_hold,
group->name());
string header;
reportEndHeader(header);
report_->print(header);
}
report_->print("\n");
}
void

View File

@ -81,7 +81,16 @@ public:
void setNoSplit(bool no_split);
ReportField *findField(const char *name);
// Header above reportPathEnd results.
void reportPathEndHeader();
// Footer below reportPathEnd results.
void reportPathEndFooter();
void reportPathEnd(PathEnd *end);
// Format report_path_endpoint only:
// Previous path end is used to detect path group changes
// so headers are reported by group.
void reportPathEnd(PathEnd *end,
PathEnd *prev_end);
void reportPathEnds(PathEndSeq *ends);
void reportPath(const Path *path);
@ -194,7 +203,8 @@ protected:
bool left_justify,
Unit *unit,
bool enabled);
void reportPathEndsEnd(PathEndSeq *ends);
void reportEndpointHeader(PathEnd *end,
PathEnd *prev_end);
void reportShort(const PathEndUnconstrained *end,
PathExpanded &expanded,
string &result);

View File

@ -118,7 +118,7 @@ typedef enum {
report_path_full_clock,
report_path_full_clock_expanded,
report_path_short,
report_path_end,
report_path_endpoint,
report_path_summary,
report_path_slack_only
} ReportPathFormat;

View File

@ -2465,6 +2465,31 @@ Sta::reportPathEnds(PathEndSeq *ends)
report_path_->reportPathEnds(ends);
}
void
Sta::reportPathEndHeader()
{
report_path_->reportPathEndHeader();
}
void
Sta::reportPathEndFooter()
{
report_path_->reportPathEndFooter();
}
void
Sta::reportPathEnd(PathEnd *end)
{
report_path_->reportPathEnd(end);
}
void
Sta::reportPathEnd(PathEnd *end,
PathEnd *prev_end)
{
report_path_->reportPathEnd(end, prev_end);
}
void
Sta::reportPath(Path *path)
{

View File

@ -870,6 +870,16 @@ public:
const Corner *corner,
const SetupHold *setup_hold,
int digits);
// Header above reportPathEnd results.
void reportPathEndHeader();
// Footer below reportPathEnd results.
void reportPathEndFooter();
// Format report_path_endpoint only:
// Previous path end is used to detect path group changes
// so headers are reported by group.
void reportPathEnd(PathEnd *end,
PathEnd *prev_end);
void reportPathEnd(PathEnd *end);
void reportPathEnds(PathEndSeq *ends);
ReportPath *reportPath() { return report_path_; }
void reportPath(Path *path);

View File

@ -1793,6 +1793,8 @@ proc get_property_cmd { cmd type_key cmd_args } {
return [liberty_library_property $object $attr]
} elseif { $object_type == "Edge" } {
return [edge_property $object $attr]
} elseif { $object_type == "PathEnd" } {
return [path_end_property $object $attr]
} else {
sta_error "$cmd unsupported object type $object_type."
}
@ -1835,6 +1837,24 @@ proc edge_property { edge property } {
}
}
proc path_end_property { path_end property } {
if { $property == "startpoint" } {
return [$path_end startpoint]
} elseif { $property == "startpoint_clock" } {
return [$path_end startpoint_clock]
} elseif { $property == "endpoint" } {
return [$path_end endpoint]
} elseif { $property == "endpoint_clock" } {
return [$path_end endpoint_clock]
} elseif { $property == "endpoint_clock_pin" } {
return [$path_end endpoint_clock_pin]
} elseif { $property == "slack" } {
return [time_sta_ui [$path_end slack]]
} else {
return ""
}
}
proc get_object_type { obj } {
set object_type [object_type $obj]
if { $object_type == "Clock" } {

View File

@ -383,5 +383,15 @@ proc report_slew_limits { corner min_max all_violators verbose nosplit } {
}
}
proc report_path_ends { path_ends } {
report_path_end_header
set prev_end "NULL"
foreach path_end $path_ends {
report_path_end2 $path_end $prev_end
set prev_end $path_end
}
report_path_end_footer
}
# sta namespace end.
}

View File

@ -138,7 +138,7 @@ proc_redirect report_clock_skew {
################################################################
define_sta_cmd_args "report_checks" \
define_sta_cmd_args "find_timing_paths" \
{[-from from_list|-rise_from from_list|-fall_from from_list]\
[-through through_list|-rise_through through_list|-fall_through through_list]\
[-to to_list|-rise_to to_list|-fall_to to_list]\
@ -150,27 +150,22 @@ define_sta_cmd_args "report_checks" \
[-slack_max slack_max]\
[-slack_min slack_min]\
[-sort_by_slack]\
[-path_group group_name]\
[-format full|full_clock|full_clock_expanded|short|end|summary]\
[-fields [capacitance|transition_time|input_pin|net]]\
[-digits digits]\
[-no_line_splits]\
[> filename] [>> filename]}
[-path_group group_name]}
proc_redirect report_checks {
global sta_report_unconstrained_paths
variable path_options
proc find_timing_paths { args } {
set path_ends [find_timing_paths_cmd "find_timing_paths" args]
return $path_ends
}
parse_key_args "report_checks" args \
proc find_timing_paths_cmd { cmd args_var } {
upvar 1 $args_var args
parse_key_args "find_timing_paths" args \
keys {-from -rise_from -fall_from -to -rise_to -fall_to \
-path_delay -corner -group_count -endpoint_count \
-slack_max -slack_min -path_group} \
flags {-sort_by_slack -unique_paths_to_endpoint} 0
parse_report_path_options "report_checks" args "full" 0
set cmd "report_checks"
set min_max "max"
set end_tr "rise_fall"
if [info exists keys(-path_delay)] {
@ -261,7 +256,36 @@ proc_redirect report_checks {
$slack_min $slack_max \
$sort_by_slack $groups \
1 1 1 1 1 1]
if { [$path_ends empty] } {
return $path_ends
}
################################################################
define_sta_cmd_args "report_checks" \
{[-from from_list|-rise_from from_list|-fall_from from_list]\
[-through through_list|-rise_through through_list|-fall_through through_list]\
[-to to_list|-rise_to to_list|-fall_to to_list]\
[-path_delay min|min_rise|min_fall|max|max_rise|max_fall|min_max]\
[-corner corner_name]\
[-group_count path_count] \
[-endpoint_count path_count]\
[-unique_paths_to_endpoint]\
[-slack_max slack_max]\
[-slack_min slack_min]\
[-sort_by_slack]\
[-path_group group_name]\
[-format full|full_clock|full_clock_expanded|short|end|summary]\
[-fields [capacitance|transition_time|input_pin|net]]\
[-digits digits]\
[-no_line_splits]\
[> filename] [>> filename]}
proc_redirect report_checks {
global sta_report_unconstrained_paths
parse_report_path_options "report_checks" args "full" 0
set path_ends [find_timing_paths_cmd "report_checks" args]
if { $path_ends == {} } {
if { $sta_report_unconstrained_paths } {
puts "No paths."
} else {
@ -270,7 +294,6 @@ proc_redirect report_checks {
} else {
report_path_ends $path_ends
}
delete_path_ends $path_ends
}
################################################################
@ -398,7 +421,6 @@ proc_redirect report_check_types {
$recovery $removal \
$clk_gating_setup $clk_gating_hold]
report_path_ends $path_ends
delete_path_ends $path_ends
}
if { $max_transition } {

View File

@ -65,6 +65,7 @@
#include "Tag.hh"
#include "PathVertex.hh"
#include "PathRef.hh"
#include "PathExpanded.hh"
#include "PathEnd.hh"
#include "PathGroup.hh"
#include "CheckTiming.hh"
@ -96,7 +97,6 @@ typedef PinSet TmpPinSet;
typedef PinSeq TmpPinSeq;
typedef InstanceSeq TmpInstanceSeq;
typedef InstanceSet TmpInstanceSet;
typedef PathEndSeq::Iterator PathEndSeqIterator;
typedef MinPulseWidthCheckSeq::Iterator MinPulseWidthCheckSeqIterator;
typedef FloatSeq TmpFloatSeq;
typedef string TmpString;
@ -1596,7 +1596,7 @@ using namespace sta;
else if (stringEq(arg, "short"))
$1 = report_path_short;
else if (stringEq(arg, "end"))
$1 = report_path_end;
$1 = report_path_endpoint;
else if (stringEq(arg, "summary"))
$1 = report_path_summary;
else if (stringEq(arg, "slack_only"))
@ -1779,11 +1779,17 @@ using namespace sta;
%typemap(out) PathEndSeq* {
Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
Tcl_SetObjResult(interp, obj);
}
%typemap(out) PathEndSeqIterator* {
Tcl_Obj *obj = SWIG_NewInstanceObj($1, $1_descriptor, false);
Tcl_SetObjResult(interp, obj);
Tcl_Obj *list = Tcl_NewListObj(0, NULL);
const PathEndSeq *path_ends = $1;
PathEndSeq::ConstIterator end_iter(path_ends);
while (end_iter.hasNext()) {
PathEnd *path_end = end_iter.next();
Tcl_Obj *obj = SWIG_NewInstanceObj(path_end, SWIGTYPE_p_PathEnd, false);
Tcl_ListObjAppendElement(interp, list, obj);
}
delete path_ends;
Tcl_SetObjResult(interp, list);
}
%typemap(out) MinPulseWidthCheckSeqIterator* {
@ -2196,20 +2202,6 @@ private:
~PathEnd();
};
class PathEndSeq
{
private:
PathEnd();
~PathEnd();
};
class PathEndSeqIterator
{
private:
PathEndSeqIterator();
~PathEndSeqIterator();
};
class MinPulseWidthCheck
{
private:
@ -4412,6 +4404,31 @@ find_path_ends(ExceptionFrom *from,
return ends;
}
void
report_path_end_header()
{
Sta::sta()->reportPathEndHeader();
}
void
report_path_end_footer()
{
Sta::sta()->reportPathEndFooter();
}
void
report_path_end(PathEnd *end)
{
Sta::sta()->reportPathEnd(end);
}
void
report_path_end2(PathEnd *end,
PathEnd *prev_end)
{
Sta::sta()->reportPathEnd(end, prev_end);
}
void
set_report_path_format(ReportPathFormat format)
{
@ -4463,18 +4480,6 @@ set_report_path_no_split(bool no_split)
Sta::sta()->setReportPathNoSplit(no_split);
}
void
report_path_ends(PathEndSeq *ends)
{
Sta::sta()->reportPathEnds(ends);
}
void
delete_path_ends(PathEndSeq *ends)
{
delete ends;
}
void
delete_path_ref(PathRef *path)
{
@ -4504,12 +4509,6 @@ report_clk_skew(ClockSet *clks,
delete clks;
}
PathEndSeqIterator *
path_end_seq_iterator(PathEndSeq *ends)
{
return new PathEndSeqIterator(ends);
}
TmpPinSet *
startpoints()
{
@ -6254,17 +6253,43 @@ Crpr common_clk_pessimism() { return self->commonClkPessimism(Sta::sta()); }
TransRiseFall *target_clk_end_trans()
{ return const_cast<TransRiseFall*>(self->targetClkEndTrans(Sta::sta())); }
Pin *
startpoint()
{
Sta *sta = Sta::sta();
PathExpanded expanded(self->path(), sta);
return expanded.startPath()->pin(sta);
}
%extend PathEndSeq {
bool empty() { return self->empty(); }
} // PathEndSeq methods
Clock *
startpoint_clock()
{
Sta *sta = Sta::sta();
return self->path()->clock(sta);
}
%extend PathEndSeqIterator {
bool has_next() { return self->hasNext(); }
PathEnd *next() { return self->next(); }
void finish() { delete self; }
} // PathEndSeqIterator methods
Pin *
endpoint()
{
Sta *sta = Sta::sta();
return self->path()->pin(sta);
}
Clock *
endpoint_clock()
{
Sta *sta = Sta::sta();
return self->targetClk(sta);
}
Pin *
endpoint_clock_pin()
{
Sta *sta = Sta::sta();
return self->targetClkPath()->pin(sta);
}
}
%extend MinPulseWidthCheckSeqIterator {
bool has_next() { return self->hasNext(); }