mirror of
https://github.com/The-OpenROAD-Project/OpenSTA.git
synced 2026-05-30 00:24:12 +08:00
latest changes 5/14 including generalize path report field and expose levelizObserver for dbsta
Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -31,6 +31,5 @@ doc/messages.txt
|
||||
# clangd turds
|
||||
.cache/
|
||||
|
||||
# test artifacts
|
||||
*/test/*.log
|
||||
**/Testing/
|
||||
# openroad ctest turds
|
||||
cmake_test*
|
||||
|
||||
59
include/sta/LevelizeObserver.hh
Normal file
59
include/sta/LevelizeObserver.hh
Normal file
@@ -0,0 +1,59 @@
|
||||
// OpenSTA, Static Timing Analyzer
|
||||
// Copyright (c) 2026, Parallax Software, Inc.
|
||||
//
|
||||
// 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/>.
|
||||
//
|
||||
// The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software.
|
||||
//
|
||||
// Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
// This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace sta {
|
||||
|
||||
class Vertex;
|
||||
class Search;
|
||||
class GraphDelayCalc;
|
||||
|
||||
// Observer fired by Levelize during (re)levelization. Downstream consumers
|
||||
// override the two hooks to invalidate caches that depend on vertex levels.
|
||||
class LevelizeObserver
|
||||
{
|
||||
public:
|
||||
virtual ~LevelizeObserver() = default;
|
||||
virtual void levelsChangedBefore() = 0;
|
||||
virtual void levelChangedBefore(Vertex *vertex) = 0;
|
||||
};
|
||||
|
||||
// Default observer installed by Sta::makeObservers. Forwards level-change
|
||||
// events to Search and GraphDelayCalc so their internal caches stay
|
||||
// consistent. Subclass and override to extend (call the base methods first,
|
||||
// then add your own invalidation).
|
||||
class StaLevelizeObserver : public LevelizeObserver
|
||||
{
|
||||
public:
|
||||
StaLevelizeObserver(Search *search, GraphDelayCalc *graph_delay_calc);
|
||||
void levelsChangedBefore() override;
|
||||
void levelChangedBefore(Vertex *vertex) override;
|
||||
|
||||
private:
|
||||
Search *search_;
|
||||
GraphDelayCalc *graph_delay_calc_;
|
||||
};
|
||||
|
||||
} // namespace sta
|
||||
@@ -73,6 +73,7 @@ class ClkSkews;
|
||||
class ReportField;
|
||||
class EquivCells;
|
||||
class StaSimObserver;
|
||||
class LevelizeObserver;
|
||||
class GraphLoop;
|
||||
|
||||
using ModeNameMap = std::map<std::string, Mode*, std::less<>>;
|
||||
@@ -83,6 +84,8 @@ using CheckErrorSeq = std::vector<CheckError*>;
|
||||
enum class CmdNamespace { sta, sdc };
|
||||
using ParasiticsNameMap = std::map<std::string, Parasitics*, std::less<>>;
|
||||
using GraphLoopSeq = std::vector<GraphLoop*>;
|
||||
using ReportFieldGetValue = std::function<std::string (const Path *path,
|
||||
const StaState *sta)>;
|
||||
|
||||
// Initialize sta functions that are not part of the Sta class.
|
||||
void initSta();
|
||||
@@ -982,16 +985,16 @@ public:
|
||||
bool clk_gating_hold);
|
||||
void setReportPathFormat(ReportPathFormat format);
|
||||
void setReportPathFieldOrder(const StringSeq &field_names);
|
||||
void setReportPathFields(bool report_input_pin,
|
||||
bool report_hier_pins,
|
||||
bool report_net,
|
||||
bool report_cap,
|
||||
bool report_slew,
|
||||
bool report_fanout,
|
||||
bool report_variation,
|
||||
bool report_src_attr,
|
||||
bool report_orig_name);
|
||||
void setReportPathFields(const StringSeq &fields);
|
||||
ReportField *findReportPathField(std::string_view name);
|
||||
ReportField *findReportPathFieldAbrev(std::string_view name);
|
||||
void makeReportPathField(std::string_view name,
|
||||
std::string_view name_abrev,
|
||||
std::string_view title,
|
||||
size_t width,
|
||||
bool left_justify,
|
||||
Unit *unit,
|
||||
const ReportFieldGetValue &get_value);
|
||||
void setReportPathDigits(int digits);
|
||||
void setReportPathNoSplit(bool no_split);
|
||||
void reportPathEnd(PathEnd *end);
|
||||
@@ -1327,6 +1330,10 @@ public:
|
||||
// Ensure a network has been read, linked and liberty libraries exist.
|
||||
Network *ensureLibLinked();
|
||||
void ensureLevelized();
|
||||
// Replace the Levelize observer. Takes ownership; deletes any prior
|
||||
// observer. Subclass StaLevelizeObserver to extend the default behavior
|
||||
// (Search + GraphDelayCalc forwarding) without re-implementing it.
|
||||
void setLevelizeObserver(LevelizeObserver *observer);
|
||||
// Ensure that the timing graph has been built.
|
||||
Graph *ensureGraph();
|
||||
void ensureClkArrivals();
|
||||
|
||||
@@ -91,6 +91,9 @@ stringEqual(std::string_view s1,
|
||||
|
||||
std::pair<float, bool>
|
||||
stringFloat(const std::string &str);
|
||||
std::pair<long long, bool>
|
||||
stringLong(const std::string &str,
|
||||
int base = 10);
|
||||
|
||||
bool
|
||||
isDigits(const char *str);
|
||||
|
||||
@@ -62,7 +62,7 @@ sta::LibertyParse::error(const location_type &loc,
|
||||
%define api.parser.class {LibertyParse}
|
||||
%define api.value.type variant
|
||||
|
||||
%expect 2
|
||||
%expect 0
|
||||
|
||||
%token <std::string> STRING KEYWORD
|
||||
%token <float> FLOAT
|
||||
@@ -75,8 +75,8 @@ sta::LibertyParse::error(const location_type &loc,
|
||||
%type <void *> statement complex_attr simple_attr variable group file
|
||||
%type <sta::LibertyAttrValueSeq *> attr_values
|
||||
%type <sta::LibertyAttrValue *> attr_value
|
||||
%type <std::string> string expr expr_term expr_term1 volt_expr
|
||||
%type <char> expr_op volt_op
|
||||
%type <std::string> string expr expr_term expr_term1
|
||||
%type <char> expr_op
|
||||
|
||||
%start file
|
||||
|
||||
@@ -157,36 +157,8 @@ string:
|
||||
;
|
||||
|
||||
attr_value:
|
||||
FLOAT
|
||||
{ $$ = reader->makeAttrValueFloat($1); }
|
||||
| expr
|
||||
expr
|
||||
{ $$ = reader->makeAttrValueString(std::move($1)); }
|
||||
| volt_expr
|
||||
{ $$ = reader->makeAttrValueString(std::move($1)); }
|
||||
;
|
||||
|
||||
/* Voltage expressions are ignored. */
|
||||
/* Crafted to avoid conflicts with expr */
|
||||
volt_expr:
|
||||
FLOAT volt_op FLOAT
|
||||
{ $$ = sta::format("{}{}{}", $1, $2, $3); }
|
||||
| string volt_op FLOAT
|
||||
{ $$ = sta::format("{}{}{}", $1, $2, $3); }
|
||||
| FLOAT volt_op string
|
||||
{ $$ = sta::format("{}{}{}", $1, $2, $3); }
|
||||
| volt_expr volt_op FLOAT
|
||||
{ $$ = sta::format("{}{}{}", $1, $2, $3); }
|
||||
;
|
||||
|
||||
volt_op:
|
||||
'+'
|
||||
{ $$ = '+'; }
|
||||
| '-'
|
||||
{ $$ = '-'; }
|
||||
| '*'
|
||||
{ $$ = '*'; }
|
||||
| '/'
|
||||
{ $$ = '/'; }
|
||||
;
|
||||
|
||||
expr:
|
||||
@@ -197,6 +169,8 @@ expr:
|
||||
|
||||
expr_term:
|
||||
string
|
||||
| FLOAT
|
||||
{ $$ = sta::format("{}", $1); }
|
||||
| '0'
|
||||
{ $$ = std::string("0"); }
|
||||
| '1'
|
||||
@@ -224,6 +198,10 @@ expr_op:
|
||||
{ $$ = '&'; }
|
||||
| '^'
|
||||
{ $$ = '^'; }
|
||||
| '-'
|
||||
{ $$ = '-'; }
|
||||
| '/'
|
||||
{ $$ = '/'; }
|
||||
;
|
||||
|
||||
semi_opt:
|
||||
|
||||
@@ -513,6 +513,13 @@ LibertyGroup::findAttrInt(std::string_view attr_name,
|
||||
exists = exists1;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
const std::string &int_str = attr_value.stringValue();
|
||||
auto [value1, valid1] = stringLong(int_str);
|
||||
value = value1;
|
||||
exists = valid1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
exists = false;
|
||||
}
|
||||
|
||||
@@ -2344,16 +2344,13 @@ LibertyReader::readReceiverCapacitance(const LibertyGroup *timing_group,
|
||||
std::string cap_group_name1 = sta::format("{}_{}", cap_group_name, rf->to_string());
|
||||
const LibertyGroup *cap_group = timing_group->findSubgroup(cap_group_name1);
|
||||
if (cap_group) {
|
||||
const LibertySimpleAttr *segment_attr = cap_group->findSimpleAttr("segment");
|
||||
if (segment_attr) {
|
||||
// For receiver_capacitance groups with mulitiple segments this
|
||||
// overrides the index passed in beginReceiverCapacitance1Rise/Fall.
|
||||
int segment;
|
||||
bool exists;
|
||||
getAttrInt(segment_attr, segment, exists);
|
||||
if (exists)
|
||||
index = segment;
|
||||
}
|
||||
// For receiver_capacitance groups with mulitiple segments this
|
||||
// overrides the index passed in beginReceiverCapacitance1Rise/Fall.
|
||||
int segment;
|
||||
bool exists;
|
||||
cap_group->findAttrInt("segment", segment, exists);
|
||||
if (exists)
|
||||
index = segment;
|
||||
TableModel *model = readTableModel(cap_group, rf, TableTemplateType::delay,
|
||||
cap_scale_, ScaleFactorType::pin_cap);
|
||||
if (ReceiverModel::checkAxes(model)) {
|
||||
@@ -3224,24 +3221,6 @@ LibertyReader::makeFloatTable(const LibertyComplexAttr *values_attr,
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
LibertyReader::getAttrInt(const LibertySimpleAttr *attr,
|
||||
// Return values.
|
||||
int &value,
|
||||
bool &exists)
|
||||
{
|
||||
value = 0;
|
||||
exists = false;
|
||||
const LibertyAttrValue &attr_value = attr->value();
|
||||
if (attr_value.isFloat()) {
|
||||
auto [float_val, valid] = attr_value.floatValue();
|
||||
value = static_cast<int>(float_val);
|
||||
exists = true;
|
||||
}
|
||||
else
|
||||
warn(1268, attr, "{} attribute is not an integer.", attr->name());
|
||||
}
|
||||
|
||||
// Get two floats in a complex attribute.
|
||||
// attr(float1, float2);
|
||||
void
|
||||
|
||||
@@ -418,10 +418,6 @@ protected:
|
||||
StateInternalValues parseStateInternalValues(StringSeq &states,
|
||||
const LibertySimpleAttr *attr);
|
||||
|
||||
void getAttrInt(const LibertySimpleAttr *attr,
|
||||
// Return values.
|
||||
int &value,
|
||||
bool &exists);
|
||||
void getAttrFloat2(const LibertyComplexAttr *attr,
|
||||
// Return values.
|
||||
float &value1,
|
||||
|
||||
@@ -655,6 +655,15 @@ get_attribute(const char *key)
|
||||
return Sta::sta()->ensureLinked()->getAttribute(self, key);
|
||||
}
|
||||
|
||||
void
|
||||
set_attribute(const char *key,
|
||||
const char *value)
|
||||
{
|
||||
sta::Sta *sta = Sta::sta();
|
||||
sta->ensureLinked();
|
||||
sta->networkReader()->setAttribute(self, key, value);
|
||||
}
|
||||
|
||||
} // Instance methods
|
||||
|
||||
%extend InstanceChildIterator {
|
||||
@@ -813,4 +822,3 @@ bool has_next() { return self->hasNext(); }
|
||||
const Pin *next() { return self->next(); }
|
||||
void finish() { delete self; }
|
||||
} // NetConnectedPinIterator methods
|
||||
|
||||
|
||||
@@ -30,11 +30,11 @@
|
||||
|
||||
#include "Graph.hh"
|
||||
#include "StaState.hh"
|
||||
#include "sta/LevelizeObserver.hh"
|
||||
|
||||
namespace sta {
|
||||
|
||||
class SearchPred;
|
||||
class LevelizeObserver;
|
||||
class GraphLoop;
|
||||
|
||||
using VertexEdgeIterPair = std::pair<Vertex*,VertexOutEdgeIterator*>;
|
||||
@@ -133,12 +133,4 @@ private:
|
||||
EdgeSeq *edges_;
|
||||
};
|
||||
|
||||
class LevelizeObserver
|
||||
{
|
||||
public:
|
||||
virtual ~LevelizeObserver() = default;
|
||||
virtual void levelsChangedBefore() = 0;
|
||||
virtual void levelChangedBefore(Vertex *vertex) = 0;
|
||||
};
|
||||
|
||||
} // namespace sta
|
||||
|
||||
@@ -81,23 +81,22 @@ hierPinsThruEdge(const Edge *edge,
|
||||
const Graph *graph);
|
||||
|
||||
ReportField::ReportField(std::string_view name,
|
||||
std::string_view name_abrev,
|
||||
std::string_view title,
|
||||
size_t width,
|
||||
bool left_justify,
|
||||
Unit *unit,
|
||||
bool enabled) :
|
||||
ReportFieldGetValue get_value) :
|
||||
name_(name),
|
||||
name_abrev_(name_abrev),
|
||||
title_(title),
|
||||
left_justify_(left_justify),
|
||||
unit_(unit),
|
||||
enabled_(enabled)
|
||||
get_value_(get_value)
|
||||
{
|
||||
setWidth(width);
|
||||
}
|
||||
|
||||
ReportField::~ReportField()
|
||||
= default;
|
||||
|
||||
void
|
||||
ReportField::setProperties(std::string_view title,
|
||||
size_t width,
|
||||
@@ -121,75 +120,85 @@ ReportField::setEnabled(bool enabled)
|
||||
enabled_ = enabled;
|
||||
}
|
||||
|
||||
std::string
|
||||
ReportField::value(const Path *path,
|
||||
const StaState *sta) const
|
||||
{
|
||||
return get_value_(path, sta);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
ReportPath::ReportPath(StaState *sta) :
|
||||
StaState(sta)
|
||||
{
|
||||
makeFields();
|
||||
setReportFields({"incr", "total", "edge", "description"});
|
||||
setDigits(2);
|
||||
setReportFields(false, false, false, false, false, false, false, false, false);
|
||||
}
|
||||
|
||||
ReportPath::~ReportPath()
|
||||
{
|
||||
delete field_description_;
|
||||
delete field_total_;
|
||||
delete field_incr_;
|
||||
delete field_capacitance_;
|
||||
delete field_slew_;
|
||||
delete field_fanout_;
|
||||
delete field_variation_;
|
||||
delete field_src_attr_;
|
||||
delete field_orig_name_;
|
||||
delete field_edge_;
|
||||
delete field_case_;
|
||||
deleteContents(fields_);
|
||||
}
|
||||
|
||||
void
|
||||
ReportPath::makeFields()
|
||||
{
|
||||
// The order corresponds to the default field order.
|
||||
field_fanout_ = makeField("fanout", "Fanout", 6, false, nullptr, true);
|
||||
field_capacitance_ = makeField("capacitance", "Cap", 6, false,
|
||||
units_->capacitanceUnit(), true);
|
||||
field_slew_ = makeField("slew", "Slew", 6, false, units_->timeUnit(),
|
||||
true);
|
||||
field_incr_ = makeField("incr", "Delay", 6, false, units_->timeUnit(),
|
||||
true);
|
||||
field_variation_ = makeField("variation", "Variation", 6, false,
|
||||
units_->timeUnit(), false);
|
||||
field_total_ = makeField("total", "Time", 6, false, units_->timeUnit(),
|
||||
true);
|
||||
field_edge_ = makeField("edge", "", 1, false, nullptr, true);
|
||||
field_case_ = makeField("case", "case", 11, false, nullptr, false);
|
||||
field_description_ = makeField("description", "Description", 36,
|
||||
true, nullptr, true);
|
||||
field_src_attr_ = makeField("src_attr", "Src Attr", 40,
|
||||
true, nullptr, true);
|
||||
field_orig_name_ = makeField("orig_name", "Orig Name", 36,
|
||||
true, nullptr, false);
|
||||
field_fanout_ = makeField("fanout", "fanout", "Fanout", 6, false, nullptr);
|
||||
field_capacitance_ = makeField("capacitance", "cap", "Cap", 6, false,
|
||||
units_->capacitanceUnit());
|
||||
field_slew_ = makeField("slew", "slew", "Slew", 6, false, units_->timeUnit());
|
||||
field_incr_ = makeField("incr", "incr", "Delay", 6, false, units_->timeUnit());
|
||||
field_variation_ = makeField("variation", "var", "Variation", 6, false,
|
||||
units_->timeUnit());
|
||||
field_total_ = makeField("total", "total", "Time", 6, false, units_->timeUnit());
|
||||
field_edge_ = makeField("edge", "edge", "", 1, false, nullptr);
|
||||
field_case_ = makeField("case", "case", "case", 11, false, nullptr);
|
||||
field_description_ = makeField("description", "desc", "Description", 36, true, nullptr);
|
||||
field_src_attr_ = makeField("src_attr", "src", "Src Attr", 40, true, nullptr);
|
||||
}
|
||||
|
||||
ReportField *
|
||||
ReportPath::makeField(std::string_view name,
|
||||
std::string_view name_abrev,
|
||||
std::string_view title,
|
||||
int width,
|
||||
size_t width,
|
||||
bool left_justify,
|
||||
Unit *unit)
|
||||
{
|
||||
return makeField(name, name_abrev, title, width, left_justify, unit, nullptr);
|
||||
}
|
||||
|
||||
ReportField *
|
||||
ReportPath::makeField(std::string_view name,
|
||||
std::string_view name_abrev,
|
||||
std::string_view title,
|
||||
size_t width,
|
||||
bool left_justify,
|
||||
Unit *unit,
|
||||
bool enabled)
|
||||
ReportFieldGetValue get_value)
|
||||
{
|
||||
ReportField *field = new ReportField(name, title, width, left_justify,
|
||||
unit, enabled);
|
||||
ReportField *field = new ReportField(name, name_abrev, title, width, left_justify,
|
||||
unit, get_value);
|
||||
fields_.push_back(field);
|
||||
field_map_[std::string(name)] = field;
|
||||
return field;
|
||||
}
|
||||
|
||||
ReportField *
|
||||
ReportPath::findField(std::string_view name) const
|
||||
ReportPath::findField(std::string_view name)
|
||||
{
|
||||
return findKey(field_map_, std::string(name));
|
||||
}
|
||||
|
||||
ReportField *
|
||||
ReportPath::findFieldAbrev(std::string_view name)
|
||||
{
|
||||
for (ReportField *field : fields_) {
|
||||
if (field->name() == name)
|
||||
const std::string &name_abrev = field->nameAbrev();
|
||||
if (name.substr(0, name_abrev.size()) == name_abrev)
|
||||
return field;
|
||||
}
|
||||
return nullptr;
|
||||
@@ -216,34 +225,38 @@ ReportPath::setReportFieldOrder(const StringSeq &field_names)
|
||||
next_fields.push_back(field);
|
||||
}
|
||||
|
||||
fields_.clear();
|
||||
for (ReportField *field : next_fields)
|
||||
fields_.push_back(field);
|
||||
fields_ = next_fields;
|
||||
}
|
||||
|
||||
void
|
||||
ReportPath::setReportFields(bool report_input_pin,
|
||||
bool report_hier_pins,
|
||||
bool report_net,
|
||||
bool report_cap,
|
||||
bool report_slew,
|
||||
bool report_fanout,
|
||||
bool report_variation,
|
||||
bool report_src_attr,
|
||||
bool report_orig_name)
|
||||
ReportPath::setReportFields(const StringSeq &fields)
|
||||
{
|
||||
report_input_pin_ = report_input_pin;
|
||||
report_hier_pins_ = report_hier_pins;
|
||||
report_net_ = report_net;
|
||||
for (ReportField *field : fields_)
|
||||
field->setEnabled(false);
|
||||
field_incr_->setEnabled(true);
|
||||
field_total_->setEnabled(true);
|
||||
field_description_->setEnabled(true);
|
||||
field_edge_->setEnabled(true);
|
||||
// These are not real fields; they are flags.
|
||||
report_input_pin_ = false;
|
||||
report_hier_pins_ = false;
|
||||
report_net_ = false;
|
||||
|
||||
field_capacitance_->setEnabled(report_cap);
|
||||
field_slew_->setEnabled(report_slew);
|
||||
field_fanout_->setEnabled(report_fanout);
|
||||
field_variation_->setEnabled(report_variation);
|
||||
field_src_attr_->setEnabled(report_src_attr);
|
||||
field_orig_name_->setEnabled(report_orig_name);
|
||||
// for debug
|
||||
field_case_->setEnabled(false);
|
||||
for (const std::string &field_name : fields) {
|
||||
if (field_name == "input_pin")
|
||||
report_input_pin_ = true;
|
||||
else if (field_name == "hierarchical_pin")
|
||||
report_hier_pins_ = true;
|
||||
else if (field_name == "net")
|
||||
report_net_ = true;
|
||||
else {
|
||||
ReportField *field = findField(field_name);
|
||||
if (field)
|
||||
field->setEnabled(true);
|
||||
else
|
||||
report_->warn(2720, "unknown path reporting field {}.", field_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2464,8 +2477,8 @@ ReportPath::reportPathLine(const Path *path,
|
||||
// Don't show capacitance field for input pins.
|
||||
if (is_driver && field_capacitance_->enabled())
|
||||
cap = graph_delay_calc_->loadCap(pin, rf, scene, min_max);
|
||||
reportLine(what, cap, slew, field_blank_, incr, field_blank_,
|
||||
time, false, early_late, rf, src_attr, orig_name, line_case);
|
||||
reportLine(what, path, cap, slew, field_blank_, incr, field_blank_,
|
||||
time, false, early_late, rf, src_attr, line_case);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2838,13 +2851,13 @@ ReportPath::reportPath6(const Path *path,
|
||||
if (field_fanout_->enabled())
|
||||
fanout = drvrFanout(vertex, scene, min_max);
|
||||
const std::string what = descriptionField(vertex);
|
||||
reportLine(what, cap, slew, fanout,
|
||||
reportLine(what, path1, cap, slew, fanout,
|
||||
incr, field_blank_, time, false, min_max, rf, src_attr,
|
||||
orig_name, line_case);
|
||||
|
||||
if (report_net_) {
|
||||
const std::string what2 = descriptionNet(pin);
|
||||
reportLine(what2, field_blank_, field_blank_, field_blank_,
|
||||
reportLine(what2, path1, field_blank_, field_blank_, field_blank_,
|
||||
field_blank_, field_blank_, field_blank_, false, min_max,
|
||||
nullptr, src_attr, "", "");
|
||||
}
|
||||
@@ -2857,7 +2870,7 @@ ReportPath::reportPath6(const Path *path,
|
||||
|| (i == path_last_index)
|
||||
|| is_clk_start) {
|
||||
const std::string what = descriptionField(vertex);
|
||||
reportLine(what, field_blank_, slew, field_blank_,
|
||||
reportLine(what, path1, field_blank_, slew, field_blank_,
|
||||
incr, field_blank_, time, false, min_max, rf, src_attr,
|
||||
orig_name, line_case);
|
||||
prev_time = time;
|
||||
@@ -2882,27 +2895,27 @@ ReportPath::reportVariation(const Path *path) const
|
||||
switch (variables_->pocvMode()) {
|
||||
case PocvMode::normal: {
|
||||
float std_dev = arc_delay.stdDev();
|
||||
reportLine("sigma", field_blank_, field_blank_, field_blank_,
|
||||
reportLine("sigma", path, field_blank_, field_blank_, field_blank_,
|
||||
field_blank_, std_dev, field_blank_, true, min_max,
|
||||
nullptr, "", "", "");
|
||||
break;
|
||||
}
|
||||
case PocvMode::skew_normal: {
|
||||
float mean = arc_delay.mean();
|
||||
reportLine("mean", field_blank_, field_blank_, field_blank_,
|
||||
reportLine("mean", path, field_blank_, field_blank_, field_blank_,
|
||||
field_blank_, mean, field_blank_, true, min_max,
|
||||
nullptr, "", "", "");
|
||||
float mean_shift = arc_delay.meanShift();
|
||||
reportLine("mean_shift", field_blank_, field_blank_, field_blank_,
|
||||
reportLine("mean_shift", path, field_blank_, field_blank_, field_blank_,
|
||||
field_blank_, mean_shift, field_blank_, true, min_max,
|
||||
nullptr, "", "", "");
|
||||
float std_dev = arc_delay.stdDev();
|
||||
reportLine("std_dev", field_blank_, field_blank_, field_blank_,
|
||||
reportLine("std_dev", path, field_blank_, field_blank_, field_blank_,
|
||||
field_blank_, std_dev, field_blank_, true, min_max,
|
||||
nullptr, "", "", "");
|
||||
// skewness is dimensionless, so scale it to the field's time units.
|
||||
float skewness = arc_delay.skewness() * units_->timeUnit()->scale();
|
||||
reportLine("skewness", field_blank_, field_blank_, field_blank_,
|
||||
reportLine("skewness", path, field_blank_, field_blank_, field_blank_,
|
||||
field_blank_, skewness, field_blank_, true, min_max,
|
||||
nullptr, "", "", "");
|
||||
break;
|
||||
@@ -2930,9 +2943,9 @@ ReportPath::reportHierPinsThru(const Path *path) const
|
||||
if (prev_edge && prev_edge->isWire()) {
|
||||
for (const Pin *hpin : hierPinsThruEdge(prev_edge, network_, graph_)) {
|
||||
const std::string what = descriptionField(hpin);
|
||||
reportLine(what, field_blank_, field_blank_, field_blank_,
|
||||
field_blank_, field_blank_, field_blank_, false, path->minMax(this),
|
||||
nullptr, "", "", "");
|
||||
reportLine(what, path, field_blank_, field_blank_, field_blank_,
|
||||
field_blank_, field_blank_, field_blank_, false,
|
||||
path->minMax(this), nullptr, "", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3124,8 +3137,8 @@ ReportPath::reportLine(std::string_view what,
|
||||
Delay total,
|
||||
const EarlyLate *early_late) const
|
||||
{
|
||||
reportLine(what, field_blank_, field_blank_, field_blank_, field_blank_,
|
||||
field_blank_, total, false, early_late, nullptr, "", "", "");
|
||||
reportLine(what, nullptr, field_blank_, field_blank_, field_blank_, field_blank_,
|
||||
field_blank_, total, false, early_late, nullptr, "", "");
|
||||
}
|
||||
|
||||
// Report negative total.
|
||||
@@ -3134,7 +3147,7 @@ ReportPath::reportLineNegative(std::string_view what,
|
||||
Delay total,
|
||||
const EarlyLate *early_late) const
|
||||
{
|
||||
reportLine(what, field_blank_, field_blank_, field_blank_,
|
||||
reportLine(what, nullptr, field_blank_, field_blank_, field_blank_,
|
||||
field_blank_, field_blank_, total, true /* tota_with_minus */,
|
||||
early_late, nullptr, "", "", "");
|
||||
}
|
||||
@@ -3146,8 +3159,8 @@ ReportPath::reportLine(std::string_view what,
|
||||
const EarlyLate *early_late,
|
||||
const RiseFall *rf) const
|
||||
{
|
||||
reportLine(what, field_blank_, field_blank_, field_blank_,
|
||||
field_blank_, field_blank_, total, false, early_late, rf, "", "", "");
|
||||
reportLine(what, nullptr, field_blank_, field_blank_, field_blank_,
|
||||
field_blank_, field_blank_, total, false, early_late, rf, "", "");
|
||||
}
|
||||
|
||||
// Report increment, and total.
|
||||
@@ -3157,8 +3170,8 @@ ReportPath::reportLine(std::string_view what,
|
||||
const Delay &total,
|
||||
const EarlyLate *early_late) const
|
||||
{
|
||||
reportLine(what, field_blank_, field_blank_, field_blank_,
|
||||
incr, field_blank_, total, false, early_late, nullptr, "", "", "");
|
||||
reportLine(what, nullptr, field_blank_, field_blank_, field_blank_,
|
||||
incr, field_blank_, total, false, early_late, nullptr, "", "");
|
||||
}
|
||||
|
||||
// Report increment, total, and transition suffix.
|
||||
@@ -3169,8 +3182,8 @@ ReportPath::reportLine(std::string_view what,
|
||||
const EarlyLate *early_late,
|
||||
const RiseFall *rf) const
|
||||
{
|
||||
reportLine(what, field_blank_, field_blank_, field_blank_,
|
||||
incr, field_blank_, total, false, early_late, rf, "", "", "");
|
||||
reportLine(what, nullptr, field_blank_, field_blank_, field_blank_,
|
||||
incr, field_blank_, total, false, early_late, rf, "", "");
|
||||
}
|
||||
|
||||
// Report slew, increment, and total.
|
||||
@@ -3181,12 +3194,13 @@ ReportPath::reportLine(std::string_view what,
|
||||
const Delay &total,
|
||||
const EarlyLate *early_late) const
|
||||
{
|
||||
reportLine(what, field_blank_, slew, field_blank_,
|
||||
incr, field_blank_, total, false, early_late, nullptr, "", "", "");
|
||||
reportLine(what, nullptr, field_blank_, slew, field_blank_,
|
||||
incr, field_blank_, total, false, early_late, nullptr, "", "");
|
||||
}
|
||||
|
||||
void
|
||||
ReportPath::reportLine(std::string_view what,
|
||||
const Path *path,
|
||||
float cap,
|
||||
const Slew &slew,
|
||||
float fanout,
|
||||
@@ -3256,6 +3270,8 @@ ReportPath::reportLine(std::string_view what,
|
||||
}
|
||||
else if (field == field_case_)
|
||||
line += line_case;
|
||||
else if (field->getValue())
|
||||
line += field->value(path, this);
|
||||
|
||||
first_field = false;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
@@ -49,9 +50,51 @@ namespace sta {
|
||||
|
||||
class Scene;
|
||||
class PathExpanded;
|
||||
class ReportField;
|
||||
|
||||
using ReportFieldGetValue = std::function<std::string (const Path *path,
|
||||
const StaState *sta)>;
|
||||
|
||||
class ReportField
|
||||
{
|
||||
public:
|
||||
ReportField(std::string_view name,
|
||||
std::string_view name_abrev,
|
||||
std::string_view title,
|
||||
size_t width,
|
||||
bool left_justify,
|
||||
Unit *unit,
|
||||
ReportFieldGetValue get_value);
|
||||
void setProperties(std::string_view title,
|
||||
size_t width,
|
||||
bool left_justify);
|
||||
const std::string &name() const { return name_; }
|
||||
const std::string &nameAbrev() const { return name_abrev_; }
|
||||
const std::string &title() const { return title_; }
|
||||
size_t width() const { return width_; }
|
||||
void setWidth(size_t width);
|
||||
bool leftJustify() const { return left_justify_; }
|
||||
Unit *unit() const { return unit_; }
|
||||
const std::string &blank() const { return blank_; }
|
||||
void setEnabled(bool enabled);
|
||||
bool enabled() const { return enabled_; }
|
||||
std::string value(const Path *path,
|
||||
const StaState *sta) const;
|
||||
const ReportFieldGetValue &getValue() const { return get_value_; }
|
||||
|
||||
protected:
|
||||
std::string name_;
|
||||
std::string name_abrev_;
|
||||
std::string title_;
|
||||
size_t width_;
|
||||
bool left_justify_;
|
||||
Unit *unit_;
|
||||
bool enabled_{false};
|
||||
ReportFieldGetValue get_value_;
|
||||
std::string blank_;
|
||||
};
|
||||
|
||||
using ReportFieldSeq = std::vector<ReportField*>;
|
||||
using ReportFieldMap = std::map<std::string, ReportField*, std::less<>>;
|
||||
|
||||
class ReportPath : public StaState
|
||||
{
|
||||
@@ -61,19 +104,12 @@ public:
|
||||
ReportPathFormat pathFormat() const { return format_; }
|
||||
void setPathFormat(ReportPathFormat format);
|
||||
void setReportFieldOrder(const StringSeq &field_names);
|
||||
void setReportFields(bool report_input_pin,
|
||||
bool report_hier_pins,
|
||||
bool report_net,
|
||||
bool report_cap,
|
||||
bool report_slew,
|
||||
bool report_fanout,
|
||||
bool report_variation,
|
||||
bool report_src_attr,
|
||||
bool report_orig_name);
|
||||
void setReportFields(const StringSeq &fields);
|
||||
int digits() const { return digits_; }
|
||||
void setDigits(int digits);
|
||||
void setNoSplit(bool no_split);
|
||||
ReportField *findField(std::string_view name) const;
|
||||
ReportField *findField(std::string_view name);
|
||||
ReportField *findFieldAbrev(std::string_view name);
|
||||
|
||||
// Header above reportPathEnd results.
|
||||
void reportPathEndHeader() const;
|
||||
@@ -166,6 +202,15 @@ public:
|
||||
float slack,
|
||||
const Scene *scene,
|
||||
const MinMax *min_max) const;
|
||||
|
||||
ReportField *makeField(std::string_view name,
|
||||
std::string_view name_abrev,
|
||||
std::string_view title,
|
||||
size_t width,
|
||||
bool left_justify,
|
||||
// nullptr for string fields.
|
||||
Unit *unit,
|
||||
ReportFieldGetValue get_value);
|
||||
ReportField *fieldSlew() const { return field_slew_; }
|
||||
ReportField *fieldFanout() const { return field_fanout_; }
|
||||
ReportField *fieldCapacitance() const { return field_capacitance_; }
|
||||
@@ -175,11 +220,11 @@ public:
|
||||
protected:
|
||||
void makeFields();
|
||||
ReportField *makeField(std::string_view name,
|
||||
std::string_view name_abrev,
|
||||
std::string_view title,
|
||||
int width,
|
||||
size_t width,
|
||||
bool left_justify,
|
||||
Unit *unit,
|
||||
bool enabled);
|
||||
Unit *unit);
|
||||
void reportEndpointHeader(const PathEnd *end,
|
||||
const PathEnd *prev_end) const;
|
||||
void reportShort(const PathEndUnconstrained *end,
|
||||
@@ -372,6 +417,7 @@ protected:
|
||||
const Delay &total,
|
||||
const EarlyLate *early_late) const;
|
||||
void reportLine(std::string_view what,
|
||||
const Path *path,
|
||||
float cap,
|
||||
const Slew &slew,
|
||||
float fanout,
|
||||
@@ -486,15 +532,14 @@ protected:
|
||||
|
||||
// Path options.
|
||||
ReportPathFormat format_{ReportPathFormat::full};
|
||||
ReportFieldSeq fields_;
|
||||
bool report_input_pin_;
|
||||
bool report_hier_pins_;
|
||||
bool report_net_;
|
||||
bool no_split_{false};
|
||||
int digits_;
|
||||
|
||||
size_t start_end_pt_width_{80};
|
||||
|
||||
ReportFieldMap field_map_;
|
||||
ReportFieldSeq fields_;
|
||||
ReportField *field_description_;
|
||||
ReportField *field_total_;
|
||||
ReportField *field_incr_;
|
||||
@@ -511,41 +556,9 @@ protected:
|
||||
std::string minus_zero_;
|
||||
|
||||
int field_width_extra_{5};
|
||||
size_t start_end_pt_width_{80};
|
||||
static constexpr float field_blank_ = -1;
|
||||
static const float field_skip_;
|
||||
};
|
||||
|
||||
class ReportField
|
||||
{
|
||||
public:
|
||||
ReportField(std::string_view name,
|
||||
std::string_view title,
|
||||
size_t width,
|
||||
bool left_justify,
|
||||
Unit *unit,
|
||||
bool enabled);
|
||||
~ReportField();
|
||||
void setProperties(std::string_view title,
|
||||
size_t width,
|
||||
bool left_justify);
|
||||
const std::string &name() const { return name_; }
|
||||
const std::string &title() const { return title_; }
|
||||
size_t width() const { return width_; }
|
||||
void setWidth(size_t width);
|
||||
bool leftJustify() const { return left_justify_; }
|
||||
Unit *unit() const { return unit_; }
|
||||
const std::string &blank() const { return blank_; }
|
||||
void setEnabled(bool enabled);
|
||||
bool enabled() const { return enabled_; }
|
||||
|
||||
protected:
|
||||
std::string name_;
|
||||
std::string title_;
|
||||
size_t width_;
|
||||
bool left_justify_;
|
||||
Unit *unit_;
|
||||
bool enabled_;
|
||||
std::string blank_;
|
||||
};
|
||||
|
||||
} // namespace sta
|
||||
|
||||
@@ -401,31 +401,46 @@ set_report_path_format(ReportPathFormat format)
|
||||
}
|
||||
|
||||
void
|
||||
set_report_path_field_order(const StringSeq &field_names)
|
||||
set_report_path_field_order(StringSeq field_names)
|
||||
{
|
||||
Sta::sta()->setReportPathFieldOrder(field_names);
|
||||
}
|
||||
|
||||
void
|
||||
set_report_path_fields(bool report_input_pin,
|
||||
bool report_hier_pins,
|
||||
bool report_net,
|
||||
bool report_cap,
|
||||
bool report_slew,
|
||||
bool report_fanout,
|
||||
bool report_variation,
|
||||
bool report_src_attr,
|
||||
bool report_orig_name)
|
||||
set_report_path_fields(StringSeq fields)
|
||||
{
|
||||
Sta::sta()->setReportPathFields(report_input_pin,
|
||||
report_hier_pins,
|
||||
report_net,
|
||||
report_cap,
|
||||
report_slew,
|
||||
report_fanout,
|
||||
report_variation,
|
||||
report_src_attr,
|
||||
report_orig_name);
|
||||
Sta::sta()->setReportPathFields(fields);
|
||||
}
|
||||
|
||||
void
|
||||
make_report_path_attr_field(std::string attr_name,
|
||||
int width)
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
sta->makeReportPathField(attr_name, attr_name, attr_name, width, true,
|
||||
nullptr,
|
||||
[attr_name] (const Path *path,
|
||||
const StaState *sta) -> std::string {
|
||||
if (path) {
|
||||
const Network *network = sta->network();
|
||||
const Pin *pin = path->pin(sta);
|
||||
const Instance *inst = network->instance(pin);
|
||||
return network->getAttribute(inst, attr_name);
|
||||
}
|
||||
else
|
||||
return "";
|
||||
});
|
||||
}
|
||||
|
||||
const char *
|
||||
find_report_path_field_abrev(const char *name)
|
||||
{
|
||||
Sta *sta = Sta::sta();
|
||||
ReportField *field = sta->findReportPathFieldAbrev(name);
|
||||
if (field)
|
||||
return field->name().c_str();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -158,8 +158,6 @@ proc find_timing_paths_cmd { cmd args_var } {
|
||||
sta_error 511 "$cmd command failed."
|
||||
}
|
||||
|
||||
check_for_key_args $cmd args
|
||||
|
||||
if { [info exists flags(-unconstrained)] } {
|
||||
set unconstrained 1
|
||||
} elseif { [info exists sta_report_unconstrained_paths] } {
|
||||
@@ -230,7 +228,6 @@ proc find_timing_paths_cmd { cmd args_var } {
|
||||
sta_error 515 "positional arguments not supported."
|
||||
}
|
||||
}
|
||||
|
||||
set path_ends [find_path_ends $from $thrus $to $unconstrained \
|
||||
$scenes $min_max \
|
||||
$group_path_count $endpoint_path_count \
|
||||
@@ -716,45 +713,26 @@ proc parse_report_path_options { cmd args_var default_format
|
||||
set path_options(num_fmt) "%.${digits}f"
|
||||
set_report_path_digits $digits
|
||||
|
||||
set report_input_pin 0
|
||||
set report_hier_pins 0
|
||||
set report_cap 0
|
||||
set report_net 0
|
||||
set report_slew 0
|
||||
set report_fanout 0
|
||||
set report_variation 0
|
||||
set report_src_attr 0
|
||||
set report_orig_name 0
|
||||
set fields {}
|
||||
if { [info exists path_options(-fields)] } {
|
||||
foreach field $path_options(-fields) {
|
||||
if { [string match "input*" $field] } {
|
||||
set report_input_pin 1
|
||||
lappend fields "input_pin"
|
||||
} elseif { [string match "hier*" $field] } {
|
||||
set report_hier_pins 1
|
||||
} elseif { [string match "cap*" $field] } {
|
||||
set report_cap 1
|
||||
lappend fields "hierarchical_pin"
|
||||
} elseif { [string match "net" $field] } {
|
||||
set report_net 1
|
||||
} elseif { [string match "slew" $field] } {
|
||||
set report_slew 1
|
||||
} elseif { [string match "fanout" $field] } {
|
||||
set report_fanout 1
|
||||
} elseif { [string match "variation" $field] } {
|
||||
set report_variation 1
|
||||
} elseif { [string match "src*" $field] } {
|
||||
set report_src_attr 1
|
||||
} elseif { [string match "orig*" $field] } {
|
||||
set report_orig_name 1
|
||||
lappend fields "net"
|
||||
} else {
|
||||
sta_warn 168 "unknown field $field."
|
||||
set field_name [find_report_path_field_abrev $field]
|
||||
if { $field_name != "" } {
|
||||
lappend fields $field_name
|
||||
} else {
|
||||
sta_warn 168 "unknown field $field."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_report_path_fields $report_input_pin $report_hier_pins $report_net \
|
||||
$report_cap $report_slew $report_fanout $report_variation $report_src_attr \
|
||||
$report_orig_name
|
||||
|
||||
set_report_path_fields $fields
|
||||
set_report_path_no_split [info exists path_options(-no_line_splits)]
|
||||
}
|
||||
|
||||
|
||||
@@ -217,19 +217,6 @@ StaSimObserver::fanoutEdgesChangeAfter(const Pin *pin)
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
class StaLevelizeObserver : public LevelizeObserver
|
||||
{
|
||||
public:
|
||||
StaLevelizeObserver(Search *search,
|
||||
GraphDelayCalc *graph_delay_calc);
|
||||
void levelsChangedBefore() override;
|
||||
void levelChangedBefore(Vertex *vertex) override;
|
||||
|
||||
private:
|
||||
Search *search_;
|
||||
GraphDelayCalc *graph_delay_calc_;
|
||||
};
|
||||
|
||||
StaLevelizeObserver::StaLevelizeObserver(Search *search,
|
||||
GraphDelayCalc *graph_delay_calc) :
|
||||
search_(search),
|
||||
@@ -2816,20 +2803,9 @@ Sta::setReportPathFieldOrder(const StringSeq &field_names)
|
||||
}
|
||||
|
||||
void
|
||||
Sta::setReportPathFields(bool report_input_pin,
|
||||
bool report_hier_pins,
|
||||
bool report_net,
|
||||
bool report_cap,
|
||||
bool report_slew,
|
||||
bool report_fanout,
|
||||
bool report_variation,
|
||||
bool report_src_attr,
|
||||
bool report_orig_name)
|
||||
Sta::setReportPathFields(const StringSeq &fields)
|
||||
{
|
||||
report_path_->setReportFields(report_input_pin, report_hier_pins, report_net,
|
||||
report_cap, report_slew, report_fanout,
|
||||
report_variation, report_src_attr,
|
||||
report_orig_name);
|
||||
report_path_->setReportFields(fields);
|
||||
}
|
||||
|
||||
ReportField *
|
||||
@@ -2838,6 +2814,24 @@ Sta::findReportPathField(std::string_view name)
|
||||
return report_path_->findField(name);
|
||||
}
|
||||
|
||||
ReportField *
|
||||
Sta::findReportPathFieldAbrev(std::string_view name)
|
||||
{
|
||||
return report_path_->findFieldAbrev(name);
|
||||
}
|
||||
|
||||
void
|
||||
Sta::makeReportPathField(std::string_view name,
|
||||
std::string_view name_abrev,
|
||||
std::string_view title,
|
||||
size_t width,
|
||||
bool left_justify,
|
||||
Unit *unit,
|
||||
const ReportFieldGetValue &get_value)
|
||||
{
|
||||
report_path_->makeField(name, name_abrev, title, width, left_justify, unit, get_value);
|
||||
}
|
||||
|
||||
void
|
||||
Sta::setReportPathDigits(int digits)
|
||||
{
|
||||
@@ -3773,6 +3767,12 @@ Sta::ensureLevelized()
|
||||
levelize_->ensureLevelized();
|
||||
}
|
||||
|
||||
void
|
||||
Sta::setLevelizeObserver(LevelizeObserver *observer)
|
||||
{
|
||||
levelize_->setObserver(observer);
|
||||
}
|
||||
|
||||
void
|
||||
Sta::updateGeneratedClks()
|
||||
{
|
||||
|
||||
@@ -265,45 +265,6 @@ using namespace sta;
|
||||
|
||||
%}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
%inline %{
|
||||
|
||||
inline bool
|
||||
is_object(const char *obj)
|
||||
{
|
||||
// _hexaddress_p_type
|
||||
const std::string s(obj);
|
||||
if (s.empty() || s[0] != '_')
|
||||
return false;
|
||||
const size_t hex_digits = sizeof(void *) * 2;
|
||||
if (s.size() < 1 + hex_digits + 3)
|
||||
return false;
|
||||
for (size_t i = 1; i < 1 + hex_digits; i++) {
|
||||
if (!std::isxdigit(static_cast<unsigned char>(s[i])))
|
||||
return false;
|
||||
}
|
||||
if (s.compare(1 + hex_digits, 3, "_p_") != 0)
|
||||
return false;
|
||||
for (size_t i = 1 + hex_digits + 3; i < s.size(); i++) {
|
||||
char ch = s[i];
|
||||
if (!(std::isalnum(ch) || ch == '_'))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Assumes is_object is true.
|
||||
inline const char *
|
||||
object_type(const char *obj)
|
||||
{
|
||||
if (is_object(obj))
|
||||
return &obj[1 + sizeof(void*) * 2 + 3];
|
||||
return "";
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SWIG type definitions.
|
||||
|
||||
@@ -66,9 +66,11 @@ proc setup {} {
|
||||
set valgrind_shared_lib_failure 0
|
||||
|
||||
if { ![file exists $app_path] } {
|
||||
error "$app_path not found."
|
||||
puts "$app_path not found."
|
||||
exit 1
|
||||
} elseif { ![file executable $app_path] } {
|
||||
error "$app_path is not executable."
|
||||
puts "$app_path is not executable."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,32 @@ stringFloat(const std::string &str)
|
||||
#endif
|
||||
}
|
||||
|
||||
std::pair<long long, bool>
|
||||
stringLong(const std::string &str,
|
||||
int base)
|
||||
{
|
||||
long long value;
|
||||
#if defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L
|
||||
auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value, base);
|
||||
|
||||
// Check for success and that we consumed the entire string
|
||||
if (ec == std::errc() && ptr == str.data() + str.size())
|
||||
return {value, true};
|
||||
else
|
||||
return {0LL, false};
|
||||
#else
|
||||
char *ptr;
|
||||
errno = 0;
|
||||
// strtoll handles "long long" specifically
|
||||
value = std::strtoll(str.data(), &ptr, base);
|
||||
|
||||
if (errno == ERANGE || *ptr != '\0' || ptr == str.data())
|
||||
return {0LL, false};
|
||||
else
|
||||
return {value, true};
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
trimRight(std::string &str)
|
||||
{
|
||||
|
||||
35
util/Util.i
35
util/Util.i
@@ -504,4 +504,39 @@ fuzzy_equal(float value1,
|
||||
return fuzzyEqual(value1, value2);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
bool
|
||||
is_object(const char *obj)
|
||||
{
|
||||
// _hexaddress_p_type
|
||||
const std::string s(obj);
|
||||
if (s.empty() || s[0] != '_')
|
||||
return false;
|
||||
const size_t hex_digits = sizeof(void *) * 2;
|
||||
if (s.size() < 1 + hex_digits + 3)
|
||||
return false;
|
||||
for (size_t i = 1; i < 1 + hex_digits; i++) {
|
||||
if (!std::isxdigit(static_cast<unsigned char>(s[i])))
|
||||
return false;
|
||||
}
|
||||
if (s.compare(1 + hex_digits, 3, "_p_") != 0)
|
||||
return false;
|
||||
for (size_t i = 1 + hex_digits + 3; i < s.size(); i++) {
|
||||
char ch = s[i];
|
||||
if (!(std::isalnum(ch) || ch == '_'))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Assumes is_object is true.
|
||||
const char *
|
||||
object_type(const char *obj)
|
||||
{
|
||||
if (is_object(obj))
|
||||
return &obj[1 + sizeof(void*) * 2 + 3];
|
||||
return "";
|
||||
}
|
||||
|
||||
%} // inline
|
||||
|
||||
Reference in New Issue
Block a user