Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,5 @@ doc/messages.txt
# clangd turds
.cache/

# test artifacts
*/test/*.log
**/Testing/
# openroad ctest turds
cmake_test*
59 changes: 59 additions & 0 deletions include/sta/LevelizeObserver.hh
Original file line number Diff line number Diff line change
@@ -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
25 changes: 16 additions & 9 deletions include/sta/Sta.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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<>>;
Expand All @@ -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();
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions include/sta/StringUtil.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
42 changes: 10 additions & 32 deletions liberty/LibertyParse.yy
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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:
Expand All @@ -197,6 +169,8 @@ expr:

expr_term:
string
| FLOAT
{ $$ = sta::format("{}", $1); }
| '0'
{ $$ = std::string("0"); }
| '1'
Expand Down Expand Up @@ -224,6 +198,10 @@ expr_op:
{ $$ = '&'; }
| '^'
{ $$ = '^'; }
| '-'
{ $$ = '-'; }
| '/'
{ $$ = '/'; }
;

semi_opt:
Expand Down
7 changes: 7 additions & 0 deletions liberty/LibertyParser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Comment on lines +516 to +522
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In LibertyGroup::findAttrInt, the else block assumes that if an attribute value is not a float, it must be a string that can be parsed as a long. However, LibertyAttrValue could potentially hold other types depending on the parser state. Accessing stringValue() without verifying the type (e.g., via isString()) might lead to undefined behavior or assertions if the value is of an unexpected type.

    else if (attr_value.isString()) {
      const std::string &int_str = attr_value.stringValue();
      auto [value1, valid1] = stringLong(int_str);
      value = value1;
      exists = valid1;
      return;
    }

}
exists = false;
}
Expand Down
35 changes: 7 additions & 28 deletions liberty/LibertyReader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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
Expand Down
4 changes: 0 additions & 4 deletions liberty/LibertyReaderPvt.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 9 additions & 1 deletion network/Network.i
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -813,4 +822,3 @@ bool has_next() { return self->hasNext(); }
const Pin *next() { return self->next(); }
void finish() { delete self; }
} // NetConnectedPinIterator methods

10 changes: 1 addition & 9 deletions search/Levelize.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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*>;
Expand Down Expand Up @@ -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
Loading