Skip to content

Sta variant corner - #394

Open
dsengupta0628 wants to merge 6 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:sta_variant_corner
Open

Sta variant corner#394
dsengupta0628 wants to merge 6 commits into
The-OpenROAD-Project:masterfrom
The-OpenROAD-Project-staging:sta_variant_corner

Conversation

@dsengupta0628

Copy link
Copy Markdown
Contributor

Summary

Adds an analysis_corner object to the OpenROAD fork of OpenSTA: a named
bundle of per-corner setup data (liberty, parasitics, corner-scoped SDC)
that scenes reference. One logical mode can now be analyzed across N
corners with different derates, IO delays, clock uncertainty, and clock
latency — without defining one composed mode per (mode x corner) pair.

Scenes remain the only unit of analysis and results; the corner is a
setup-side object that computes nothing. The timing engine's data model
is unchanged.

Motivation

Per-corner data (derates, IO budgets from external blocks, uncertainty)
lives in SDC, but SDC state is per-mode. Without this feature, MCMM
flows must compose mode+corner SDC files into synthetic modes
(func_ss, func_ff, ...): each is a full Mode instance (own Sdc,
Sim, ClkNetwork, Genclks, PathGroups) duplicated per corner, and
the generated mode names surface in report_checks headers, get_modes,
write_sdc -mode, and every downstream script. This PR removes the need
for synthetic modes entirely: reports show the real mode, and the mode
list stays clean.

User-facing commands

  define_analysis_corner ss_cmax -liberty {ss.lib} -spef spef_ss \
    -sdc {derates_ss.sdc io_ss.sdc}
  read_sdc -mode func func.sdc
  define_scene func_ss -mode func -analysis_corner ss_cmax

  get_analysis_corners [pattern]
  get_scenes -filter {analysis_corner == ss_cmax}
  get_property [get_scenes func_ss] analysis_corner

  # corner-scoped SDC, file or interactive:
  read_sdc -mode func -analysis_corner ss_cmax extra_derates.sdc
  set_cmd_analysis_corner ss_cmax ... unset_cmd_analysis_corner

All existing commands and arguments are unchanged; flows that do not use
corners are unaffected.

Design

  • Corner-scoped SDC is stored in a sparse overlay Sdc instance per
    (mode, corner), lazily created. The overlay reuses the existing Sdc
    class unmodified; whitelisted SDC write commands target it when a
    corner scope is active, and engine read points consult it with the
    mode Sdc as fallback.
  • Whitelist (corner scope): set_timing_derate, set_input_delay /
    set_output_delay, set_clock_uncertainty (all forms),
    set_clock_latency / insertion, and their unset forms. Everything
    else — clocks, exceptions, case analysis, and all other mode-level SDC
    writes — errors out in corner scope, so structural constraints can
    never exist in two places.
  • No precedence scheme. A corner either defines a data family (and
    replaces the mode's wholesale for that family's key — whole derate
    table, per pin for IO delays, per clock for uncertainty, per lookup
    for latency) or it doesn't (mode value applies untouched). No merging.
    The only ordering is the pin-beats-clock specificity the code already
    has; a corner value wins only at equal specificity.
  • Clock-form set_clock_uncertainty is stored on the corner keyed by
    the mode's Clock* (upstream stores it on the Clock object, which is
    shared across corners); purged on remove_clock / sta::clear.
  • Property/filtering is registered in the C++ property registry — no
    Tcl-side filtering.

Implementation / merge hygiene

  • Logic lives in new files: include/sta/AnalysisCorner.hh,
    search/AnalysisCorner.cc, search/AnalysisCorner.i,
    tcl/AnalysisCorner.tcl.
  • Existing files receive small fenced hunks
    (// ---- OpenROAD fork: analysis_corner support ----) intended to be
    re-applied on upstream merges: read-point overrides in
    search/Search.cc, PathEnd.cc, VisitPathEnds.cc, CheckTiming.cc,
    ReportPath.cc, one-line cmdSdc() -> cmdCornerSdc() swaps in the
    whitelisted write commands in sdc/Sdc.i, and one inline accessor in
    Sdc.hh. Sdc.cc is untouched.
  • Zero overhead when the feature is unused: overlay lookups are gated on
    inline null/empty checks; no overlays are allocated unless a
    corner-scoped SDC command runs.

Testing

  • New regressions: search_analysis_corner (define/query/filter,
    liberty-spef bundles, composition), search_corner_sdc_derate
    (1 mode x N corners vs equivalent synthetic-mode setup — exact
    numeric equivalence asserted in-test), search_corner_sdc_bundle
    (full whitelist per corner vs manual composition — exact equivalence;
    corner isolation; guard errors; redefine semantics).
  • Full suite: 6087/6087 pass.

Known limitations (documented)

  • set_input_transition / set_load are not yet corner-scoped (error
    3711); per-corner RC is expected via per-corner SPEF.
  • Corner clock latency does not adjust generated-clock master-path
    computation or the clock arrival implied by corner IO delays'
    -clock reference (mode values used).
  • A corner cannot un-constrain a pin the mode constrains (override is
    wholesale per pin, no tombstones).
  • Scenes must be defined before timing queries (pre-existing issue
    found in upstream behavior, not introduced here).

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
…pt set_ip_trans and set_load

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
@dsengupta0628 dsengupta0628 self-assigned this Jul 27, 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 introduces support for analysis corners (PVT/RC corners) to group scenes sharing operating points across modes and manage corner-scoped SDC constraints like timing derates, IO delays, clock uncertainty, and clock latency. The feedback suggests optimizing string lookups in AnalysisCornerNameMap by accepting std::string_view in makeAnalysisCorner and findAnalysisCorner to avoid unnecessary allocations, and improving the time complexity of unique delay set collection in cornerDelaySets from O(N^2) to O(N log N) using std::set.

Comment thread include/sta/Sta.hh Outdated
Comment on lines +155 to +156
AnalysisCorner *makeAnalysisCorner(const std::string &name);
AnalysisCorner *findAnalysisCorner(const std::string &name) const;

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

You're using std::less<> for AnalysisCornerNameMap in AnalysisCorner.hh, which enables heterogeneous lookups (e.g., with std::string_view) to avoid std::string allocations on lookups. To fully leverage this, consider changing the signatures of makeAnalysisCorner and findAnalysisCorner to accept std::string_view instead of const std::string &.

  AnalysisCorner *makeAnalysisCorner(std::string_view name);
  AnalysisCorner *findAnalysisCorner(std::string_view name) const;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Applied. makeAnalysisCorner/findAnalysisCorner now take std::string_view; find goes through findStringKey (the exact idiom upstream's Sta::findMode(std::string_view) uses

Comment thread search/AnalysisCorner.cc
Comment on lines +224 to +232
std::vector<DelaySet*> delay_sets;
for (const Scene *scene : mode->scenes()) {
DelaySet *delays = cornerDelaysLeafPin(scene, pin, delays_leaf_pin);
if (delays
&& std::find(delay_sets.begin(), delay_sets.end(), delays)
== delay_sets.end())
delay_sets.push_back(delays);
}
return delay_sets;

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 current implementation to find unique DelaySet* uses std::find within a loop over scenes, which has a time complexity of O(N^2) where N is the number of scenes. For better performance, especially if a mode could have a large number of scenes, consider using std::set to collect unique pointers, which would have a complexity of O(N log N).

  std::set<DelaySet*> unique_delay_sets;
  for (const Scene *scene : mode->scenes()) {
    if (auto* delays = cornerDelaysLeafPin(scene, pin, delays_leaf_pin)) {
      unique_delay_sets.insert(delays);
    }
  }
  return {unique_delay_sets.begin(), unique_delay_sets.end()};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

declined: N <= scenes-per-mode (~10), distinct sets bounded by corner count, so linear dedup beats std::set's allocations at this scale, and std::set's pointer-order iteration would make enumeration order nondeterministic across runs, which we avoid in the timing engine.

@dsengupta0628

Copy link
Copy Markdown
Contributor Author

The following work currently:

  get_scenes -filter {analysis_corner == ss}      ;# works
  get_scenes -filter {analysis_corner =~ s*}      ;# works
  get_property [get_scenes func_ss] analysis_corner

The corner itself isn't in the property dispatch: no PropertyRegistry<const AnalysisCorner*>, so the following don't work yet:

  get_analysis_corners -filter {name =~ ss*}     ;# NOT supported (pattern arg only)
  get_property $corner liberty_files             ;# errors
  define_property -object_type analysis_corner   ;# error 2209
  set_property $corner temperature 125           ;# error 2214

Signed-off-by: dsengupta0628 <dsengupta@precisioninno.com>
@dsengupta0628
dsengupta0628 requested a review from jhkim-pii July 27, 2026 14:59
hxshen97 pushed a commit to hxshen97/OpenSTA that referenced this pull request Aug 6, 2026
* Fir for write_verilog issue 3826

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

* staToVerilog2 remove escaped_name+=ch

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

* updated regression to remove \ from module name

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

* Using helpers.tcl function to redirect results

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

* add std::string and remove trailing space, update regression name

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

* update regression to reflect correct output verilog name

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

---------

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

@gadfort gadfort left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't see how the write_sdc -corner / -scene would be handled?

@@ -0,0 +1,208 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this copyright should be OpenSTA authors or something right?

Comment on lines +196 to +199
StringSeq liberty_min_files_;
StringSeq liberty_max_files_;
std::string spef_min_name_;
std::string spef_max_name_;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this is duplicated with the scene information. Can a scene now define additional liberty files from the corner?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

These members aren't a duplicate of scene state - they're at a different stage of the pipeline. The corner holds authoring inputs (file paths and spef names, plain strings, nothing loaded); the Scene holds engine state (the loaded LibertyLibrary objects and the per-scene liberty mapping, which LibertyLibrary::makeSceneMap requires to be keyed by scene->libertyIndex() - that storage can't move). It's the same relationship as read_spef -name, which parks a parasitics under a name before any scene binds it.

The point of keeping the strings on the corner is reuse across modes - a corner is authored once and expanded into N scenes:

define_analysis_corner ss_cmax -liberty {ss.lib} -spef spef_ss
define_scene func_ss -mode func -analysis_corner ss_cmax
define_scene test_ss -mode test -analysis_corner ss_cmax
define_scene scan_ss -mode scan -analysis_corner ss_cmax

Without the bundle, those file lists get re-typed per scene - the duplication just moves into user scripts, which is what this feature removes.

And no, a scene cannot add liberty files on top of the corner's. Composition is all-or-nothing per family: if define_scene has any explicit -liberty* key, the corner's liberty bundle is ignored entirely for that scene (explicit wins, no union); same independently for -spef*. A corner with no liberty bundle and no explicit -liberty errors out before the scene is created. So for any scene there is exactly one unambiguous source per family - never a merge whose provenance is unclear.

Comment on lines +196 to +203
StringSeq liberty_min_files_;
StringSeq liberty_max_files_;
std::string spef_min_name_;
std::string spef_max_name_;
// Corner-scoped SDC files, applied to the (mode, corner) overlay Sdc
// when the first scene naming this corner is defined
// (see tcl/AnalysisCorner.tcl).
StringSeq sdc_files_;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why are all these strings? seems like that could be very brittle.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

These are strings because they're just the arguments define_scene would have received - the corner saves them and replays them when a scene is defined. Nothing can be resolved earlier than that: liberty files are loaded per scene, a spef name is looked up when the scene is created (same as upstream's define_scene -spef_min , which is already a string name), and the SDC files can't be applied until a scene tells us which mode to pair the corner with.

A bad path or unknown name fails at scene creation with the same error as if the user had typed it directly - no new failure mode.

Storing pointers instead would be worse: re-reading a spef or deleting a library would leave corners holding dangling pointers, and it would force read_spef to happen before define_analysis_corner. Strings avoid both.

Comment thread include/sta/Sdc.hh
Comment on lines +1432 to +1441
public:
// True when any timing derates are set. Used to decide whether an
// analysis corner overlay Sdc overrides the mode Sdc for derate queries.
bool hasDeratingFactors() const
{
return derating_factors_ != nullptr
|| !net_derating_factors_.empty()
|| !inst_derating_factors_.empty()
|| !cell_derating_factors_.empty();
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

could this be upstreamed?

Comment thread sdc/Sdc.i
{
Sta *sta = Sta::sta();
Sdc *sdc = sta->cmdSdc();
Sdc *sdc = sta->cmdCornerSdc(); // OpenROAD fork: analysis_corner write scope.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

so these items are only stored in the corner?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No. This stores to the corner only when a corner scope is explicitly active. cmdCornerSdc() is:

Sdc *
Sta::cmdCornerSdc() const
{
if (cmd_analysis_corner_)
return cmd_mode_->makeCornerSdc(cmd_analysis_corner_);
return cmd_mode_->sdc();
}

cmd_analysis_corner_ is only set inside read_sdc -analysis_corner or after set_cmd_analysis_corner. Outside that, this returns the mode's Sdc - identical behavior to cmdSdc(), and the per-corner storage is never even allocated.

So

  • A plain set_clock_latency --> stored on the mode, all scenes see it, exactly as before.
  • Same set_clock_latency inside a corner-scoped SDC file --> stored in that (mode, corner) overlay, and at lookup time the overlay value overrides the mode's one for scenes using that corner, with fallback to the mode value otherwise.

Comment thread search/AnalysisCorner.cc
@@ -0,0 +1,446 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

copyright?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will check with Matt. Even though I wrote every line here, it still interacts with rest of OpenSTA. Will need to check how the copyright will look like (also in light of the acquisition now! This PR predates the closing date)

Comment thread search/AnalysisCorner.cc
AnalysisCorner *
Sta::makeAnalysisCorner(std::string_view name)
{
AnalysisCorner *corner = findAnalysisCorner(name);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

should this error out if you try to make one with the same name?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No, and on purpose. If the name already exists you get the existing corner back, so running the same setup script twice doesn't error. Modes work the same way (reusing a mode name is silent), and define_scene doesn't check for duplicate names at all.

Defining it again with new files replaces the old files completely - there's no partial merge, so nothing stale is left behind.

One gap: scenes that were already built from the old definition keep it; they aren't updated. I can add a warning for that case if you think that would be useful.

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.

2 participants