Skip to content

Sta upstream 10/05 - #422

Open
dsengupta0628 wants to merge 18 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:sta_update_1005
Open

dsengupta0628 wants to merge 18 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:sta_update_1005

Conversation

@dsengupta0628

Copy link
Copy Markdown
Collaborator

Latest STA from 10/05 morning upstream

jjcherry56 and others added 18 commits September 24, 2026 12:51
Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com>
Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com>
Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com>
Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com>
Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com>
Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com>
Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com>
Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com>
Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com>
Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com>
…(#521)

* fix for issue 389

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>

* use libs from test/ dir not examples/

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>

* Remove port_direction_reinit test

initSta() is intended to be called once per process; the test
exercised unsupported re-initialization.

* Remove PortDirection::init/destroy

Singletons are static objects; nothing to allocate or free.

---------

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
* Report the tcl traceback for an error while reading a file

include_file reads a file one complete command at a time and evaluates
each with catch, so the errorInfo tcl builds for a command that fails is
only reachable there.  It is discarded, and the error is reported as a
single line naming the file and line but not the procs that raised it.

sta_error_traceback, 0 by default, keeps it.  catch is given its options
dict and -errorinfo is used in place of the bare result; since errorInfo
begins with the error message, the existing check that prepends the file
and line only once is unaffected, and both the sta_continue_on_error
report and the raise at the end of the file pick it up.  The uplevel
frames include_file itself adds are trimmed from the end so the
traceback ends at the command read from the file.

With the variable at 0 the reported text is unchanged.

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>

* Report the start line of an incomplete command

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>

* Report the start line of a multi-line command that failed

include_line counts lines as they are read, so by the time a command
spanning several lines runs it names the command's last line.  An error
inside an if that opens on line 3 and closes on line 7 was reported as
line 7, and a warning from a command continued across lines 14 to 18 as
line 18.

cmd_start_line holds the first line of the command being accumulated.
include_file reports it, and sdc_file_line returns it so the messages
raised with a msg id while reading a file use it as well.

Single line commands are unaffected.  write_path_spice_arc_sense.ok is
the one golden whose warning moves, from the last line of the command
to the line the command starts on.

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>

* Report a multi-line command error at its first line

When reading stops at the first error, include_file built the message
with include_line, the last line of the failing command. Use
cmd_start_line, as the continue-on-error message and sdc_file_line do.
Remove the sta_error_traceback docs and test, superseded by the
$errorInfo traceback in 1b066c3.

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>

* Clear include_error_info at the start of include_file

include_file saves the failing command's traceback in
include_error_info, but only include clears it. read_sdc calls
include_file directly, so after a failed read_sdc the traceback was
left behind and reused by a later include error with no traceback of
its own (e.g. cannot open the file).

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>

---------

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com>
Signed-off-by: James Cherry <34749589+jjcherry56@users.noreply.github.com>
PortDirection::init/destroy were removed in f3cd9b1 (#521); the
directions are now static objects that need no initialization.
Drop the stale init() calls and the null-check guards around them.

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
MultiDrvrNet::dcalcDrvr/setDcalcDrvr were removed in bdd66c5
("dcalc multi-drvr do not use levels") and replaced by
loadSlewsInited/setLoadSlewsInited. Retarget the accessor tests
to the new API.

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
Sta::findDelays() now passes Graph::vertex_level_max instead of
levelize_->maxLevel() (e3326b0), so the delay_calc debug message
reports level 16777215. Timing results are unchanged.

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
@dsengupta0628 dsengupta0628 self-assigned this Oct 6, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request refactors several core components of the timing analysis engine, including replacing dynamic PortDirection singletons with static constexpr instances, optimizing levelization with lazy initialization, adding port limit calculations to timing models, and improving Tcl error tracebacks. However, several critical issues were identified in the review: a performance bottleneck in findCrpr due to heap allocations and O(N log N) complexity from std::map, a typo in Levelize::findBackEdges that iterates over roots_ instead of the sorted roots sequence (introducing non-determinism), a bug in zeroSlewAndWireDelays where passing true for merge prevents proper zeroing, and variable shadowing of pin in setPortLimits.

Comment thread search/Crpr.cc
Comment on lines +211 to 229
std::map<const Pin*, const Path*> src_pin_map;
const Path *src = src_clk_path1;
while (src && !src->isNull()) {
src_pin_map[src->pin(this)] = src;
src = src->prevPath();
}

const Path *tgt = tgt_clk_path1;
src = nullptr;
Pin *tgt_pin = nullptr;
while (tgt && !tgt->isNull()) {
tgt_pin = tgt->pin(this);
auto src_itr = src_pin_map.find(tgt_pin);
if (src_itr != src_pin_map.end()) {
src = src_itr->second;
break;
}
tgt = tgt->prevPath();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Using std::map here introduces $O(N \log N)$ complexity and, more importantly, performs heap allocations for every node in the path. Since findCrpr is called frequently during timing analysis, this can be a significant performance bottleneck.\n\nSince the clock path is a tree and prevPath() acts as a parent pointer, finding the common pin is equivalent to finding the Lowest Common Ancestor (LCA) of two nodes in a tree. We can achieve this in $O(N)$ time and $O(1)$ auxiliary space without any heap allocations by using the standard two-pointer technique (aligning the path depths first and then walking in lockstep).

  int src_len = 0;
  const Path *src_curr = src_clk_path1;
  while (src_curr && !src_curr->isNull()) {
    src_len++;
    src_curr = src_curr->prevPath();
  }

  int tgt_len = 0;
  const Path *tgt_curr = tgt_clk_path1;
  while (tgt_curr && !tgt_curr->isNull()) {
    tgt_len++;
    tgt_curr = tgt_curr->prevPath();
  }

  const Path *src = src_clk_path1;
  const Path *tgt = tgt_clk_path1;
  if (src_len > tgt_len) {
    for (int i = 0; i < src_len - tgt_len; ++i) {
      src = src->prevPath();
    }
  } else if (tgt_len > src_len) {
    for (int i = 0; i < tgt_len - src_len; ++i) {
      tgt = tgt->prevPath();
    }
  }

  Pin *tgt_pin = nullptr;
  while (src && !src->isNull() && tgt && !tgt->isNull()) {
    if (src->pin(this) == tgt->pin(this)) {
      tgt_pin = tgt->pin(this);
      break;
    }
    src = src->prevPath();
    tgt = tgt->prevPath();
  }

Comment thread search/Levelize.cc
Comment on lines +245 to +246
VertexSeq roots = sortedRootsWithFanout();
for (Vertex *vertex : roots_) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

There is a typo here: the code initializes a local roots sequence sorted by fanout to ensure deterministic traversal, but then iterates over the member variable roots_ instead. Since roots_ is a VertexSet (which may be ordered by pointer addresses or unordered), iterating over it directly can introduce non-determinism in the DFS traversal and loop-breaking order.

  VertexSeq roots = sortedRootsWithFanout();
  for (Vertex *vertex : roots) {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

likely typo upstream- needs fixing

Comment thread dcalc/GraphDelayCalc.cc
Comment on lines +1521 to +1523
annotateLoadDelaySlew(drvr_vertex, wire_edge, delay_zero,
load_vertex, delay_zero,
true, rf, min_max, ap_index);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

In zeroSlewAndWireDelays, passing true for the merge parameter to annotateLoadDelaySlew is a bug. The purpose of this function is to unconditionally reset/zero the downstream wire delays and slews (as done in the original code). If merge is true, the values will only be updated if they satisfy the delayGreater condition, which prevents them from being reset to 0.0 if they already have non-zero values. This should be false to match the original unconditional zeroing behavior.

          annotateLoadDelaySlew(drvr_vertex, wire_edge, delay_zero,
                                load_vertex, delay_zero,
                                false, rf, min_max, ap_index);

Comment thread search/MakeTimingModel.cc
Comment on lines +249 to +250
const Pin *pin = pin_iter->next();
const LibertyPort *port = network_->libertyPort(pin);

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

The loop variable pin shadows the function parameter pin. Renaming the loop variable to conn_pin avoids shadowing and improves code clarity.

    const Pin *conn_pin = pin_iter->next();
    const LibertyPort *port = network_->libertyPort(conn_pin);

@maliberty

Copy link
Copy Markdown
Member

Have you checked for QoR impact?

@dsengupta0628

Copy link
Copy Markdown
Collaborator Author

Have you checked for QoR impact?

not yet- there seems to be a correctness issue that I have raised with upstream. will wait for that before overloading CI with runs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants