Skip to content
Draft
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
52 changes: 52 additions & 0 deletions src/dbSta/include/db_sta/PocvDerate.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2019-2025, The OpenROAD Authors

// Parametric statistical on-chip-variation (POCV / LVF) derate, first slice.
//
// This is an OpenROAD-side (dbSta) feature that re-uses public OpenSTA APIs to
// recompute a STATISTICAL (root-sum-square / quadrature) setup slack on
// already-found critical paths. It does NOT modify the OpenSTA forward search:
// parametric-POCV variation combines in quadrature, which does not compose with
// the additive arrival sum the search propagates, so a per-arc propagation hook
// (like AOCV) is not the right place for it.

#pragma once

#include <string>

#include "sta/Search.hh" // OpenROAD-fork: POCV -- Search::PocvSigma state holder

namespace sta {

class Sta;
class PathEnd;

// One row of the POCV slack report for a single path end.
struct PocvPathResult
{
std::string endpoint;
int logic_depth = 0; // number of combinational data-path stages
float flat_slack = 0.0f; // slack as graded by flat OCV (baseline)
float pocv_slack = 0.0f; // statistical (RSS) slack
float flat_sigma = 0.0f; // linear (fully-correlated) path sigma: k*sum(d_i)
float rss_sigma = 0.0f; // quadrature path sigma: k*sqrt(sum(d_i^2))
float n_sigma = 0.0f; // sign-off sigma multiple used
};

// Recompute the parametric-POCV statistical setup slack for one path end.
//
// For each combinational data-path stage i with raw (un-derated) delay d_i and
// per-stage fractional sigma k, the path variation combines in QUADRATURE:
// sigma_rss = k * sqrt( sum_i d_i^2 )
// versus the fully-correlated linear sigma a flat derate implies:
// sigma_lin = k * sum_i d_i
// The reported POCV slack replaces the linear pessimism with N_sigma*sigma_rss:
// pocv_slack = flat_slack + N_sigma*(sigma_lin - sigma_rss)
// Because sigma_rss <= sigma_lin (equal only for a single stage), POCV slack is
// >= flat slack, and the gap GROWS with depth as sqrt(N) vs N. When the sigma
// state is inactive (default), pocv_slack == flat_slack exactly.
PocvPathResult pocvAdjustPathEnd(Sta* sta,
PathEnd* path_end,
const Search::PocvSigma& sigma);

} // namespace sta
1 change: 1 addition & 0 deletions src/dbSta/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ add_library(dbSta_lib
dbReadVerilog.cc
SpefWriter.cc
IpChecker.cc
PocvDerate.cc
)

target_include_directories(dbSta_lib
Expand Down
93 changes: 93 additions & 0 deletions src/dbSta/src/PocvDerate.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2019-2025, The OpenROAD Authors

#include "db_sta/PocvDerate.hh"

#include <cmath>

#include "sta/Delay.hh"
#include "sta/Graph.hh"
#include "sta/MinMax.hh"
#include "sta/Network.hh"
#include "sta/Path.hh"
#include "sta/PathEnd.hh"
#include "sta/PathExpanded.hh"
#include "sta/Search.hh"
#include "sta/Sta.hh"
#include "sta/TimingArc.hh"
#include "sta/TimingRole.hh"

namespace sta {

PocvPathResult pocvAdjustPathEnd(Sta* sta,
PathEnd* path_end,
const Search::PocvSigma& sigma)
{
PocvPathResult result;
const Path* end_path = path_end->path();
result.endpoint = sta->network()->pathName(end_path->pin(sta));
result.flat_slack = delayAsFloat(path_end->slack(sta));
result.pocv_slack = result.flat_slack;
result.n_sigma = sigma.n_sigma;

Graph* graph = sta->graph();
PathExpanded expanded(end_path, sta);
const size_t size = expanded.size();
// path(0) is the path root (startpoint side); path(size-1) is the endpoint.

// Walk combinational data-path stages, accumulating both the linear sum of
// raw stage delays and the sum of their squares (for the quadrature sigma).
int logic_depth = 0;
double sum_delay = 0.0; // sum_i d_i -> linear (correlated) sigma
double sum_delay_sq = 0.0; // sum_i d_i^2 -> RSS (independent) sigma
for (size_t i = 1; i < size; i++) {
const Path* p = expanded.path(i);
const Path* prev = p->prevPath();
const TimingArc* arc = p->prevArc(sta);
const Edge* edge = p->prevEdge(sta);
if (arc == nullptr || edge == nullptr || prev == nullptr) {
continue;
}
// Only the combinational logic stages carry per-stage gate variation; clock
// / reg / latch / check arcs keep their flat treatment (matches AOCV
// slice).
if (arc->role() != TimingRole::combinational()) {
continue;
}
const DcalcAPIndex dcalc_ap = p->dcalcAnalysisPtIndex(sta);
// Raw (un-derated) arc delay: the nominal stage delay d_i.
const double d = delayAsFloat(graph->arcDelay(edge, arc, dcalc_ap));
if (d <= 0.0) {
continue; // negative/zero arcs contribute no meaningful variation
}
logic_depth++;
sum_delay += d;
sum_delay_sq += d * d;
}
result.logic_depth = logic_depth;

// Per-stage fractional sigma k. Linear (fully-correlated) path sigma is the
// straight sum; quadrature (independent) path sigma is the root-sum-square.
const double k = sigma.per_stage;
result.flat_sigma = static_cast<float>(k * sum_delay);
result.rss_sigma = static_cast<float>(k * std::sqrt(sum_delay_sq));

// Inactive (default) => POCV slack == flat slack exactly (baseline-safe).
if (!sigma.active() || logic_depth == 0) {
return result;
}

// The flat grading effectively charged N_sigma * sigma_lin of pessimism to
// the data path. Parametric POCV charges only N_sigma * sigma_rss. Since
// sigma_rss <= sigma_lin, recovering the difference improves setup slack:
// pocv_slack = flat_slack + N_sigma * (sigma_lin - sigma_rss)
// For min/hold paths the early variation subtracts from arrival, so reduced
// pessimism likewise relaxes the (required - arrival) hold check in the same
// slack-improving direction; the magnitude is identical, hence one formula.
const double recovered
= sigma.n_sigma * (result.flat_sigma - result.rss_sigma);
result.pocv_slack = static_cast<float>(result.flat_slack + recovered);
return result;
}

} // namespace sta
228 changes: 228 additions & 0 deletions src/dbSta/src/dbSta.i
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
#include "odb/PtrSetMap.h"
#include "db_sta/dbSta.hh"
#include "db_sta/dbNetwork.hh"
#include "db_sta/PocvDerate.hh"
#include "sta/PocvMode.hh" // OpenROAD-fork: LVF -- PocvMode for propagation
#include "sta/TableModel.hh" // OpenROAD-fork: LVF-lib -- LVF sigma table detect
#include "sta/TimingArc.hh" // OpenROAD-fork: LVF-lib -- arc -> gate model
#include "sta/TimingRole.hh" // OpenROAD-fork: LVF-lib -- combinational filter
#include "db_sta/IpChecker.hh"
#include "db_sta/MakeDbSta.hh"
#include "ord/OpenRoad.hh"
Expand Down Expand Up @@ -314,6 +319,229 @@ bool parasitics_annotated(Pin *pin, Scene *scene) {
return parasitics->findParasiticNetwork(pin) != nullptr;
}

// Parametric statistical OCV (POCV / LVF) derate, first slice (report-only).
// Variation combines in QUADRATURE (root-sum-square)
// along the path, so this is intentionally a report-only recompute on already
// found paths, NOT a forward-search propagation hook.

// Process-global POCV sigma state, mirrored into Search::PocvSigma. Default
// (enabled=false, per_stage=0, n_sigma=0) == feature inactive == baseline.
static Search::PocvSigma &
pocvSigmaState()
{
static Search::PocvSigma sigma;
return sigma;
}

// OpenROAD-fork: POCV -- push the current sigma state onto Search. This only
// updates a default-OFF state holder that NO propagation-path code reads, so it
// never changes timing; it exists so the state lives with the timer and the
// report can read it back. No arrival invalidation is needed (search unchanged).
static void
pocvSyncState()
{
ord::OpenRoad *openroad = ord::getOpenRoad();
sta::dbSta *sta = openroad->getSta();
sta->search()->setPocvSigma(pocvSigmaState());
}
Comment on lines +340 to +346

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Add a null check for sta and sta->search() before calling setPocvSigma to prevent a potential crash if STA is not fully initialized.

static void
pocvSyncState()
{
  ord::OpenRoad *openroad = ord::getOpenRoad();
  sta::dbSta *sta = openroad->getSta();
  if (sta != nullptr && sta->search() != nullptr) {
    sta->search()->setPocvSigma(pocvSigmaState());
  }
}


// Set the per-stage fractional sigma (k) and the sign-off sigma multiple
// (n_sigma) and enable POCV. With k==0 or n_sigma==0 the feature stays inactive
// (POCV slack == flat slack).
void
pocv_sigma_set(float per_stage,
float n_sigma)
{
Search::PocvSigma &s = pocvSigmaState();
s.enabled = true;
s.per_stage = per_stage;
s.n_sigma = n_sigma;
pocvSyncState();
}

// OpenROAD-fork: LVF -- enable PROPAGATION-TIME POCV. In addition to the
// report-only state, this flips the timer into statistical (normal) delay-ops
// with the sign-off quantile = n_sigma, so the synthetic per-stage variance
// injected in Search::deratedDelayData accumulates in quadrature through the
// forward search and is read out as mean +/- n_sigma*sqrt(var) at the checks.
// Switching the mode invalidates arrivals (handled by Sta::setPocvMode).
void
pocv_sigma_set_propagate(float per_stage,
float n_sigma)
{
Search::PocvSigma &s = pocvSigmaState();
s.enabled = true;
s.per_stage = per_stage;
s.n_sigma = n_sigma;
s.propagate = true;
pocvSyncState();

ord::OpenRoad *openroad = ord::getOpenRoad();
sta::dbSta *sta = openroad->getSta();
// n_sigma is the sign-off quantile used by DelayOpsNormal::asFloat.
sta->setPocvQuantile(n_sigma);
// Statistical readout of the accumulated variance.
sta->setPocvMode(sta::PocvMode::normal);
}
Comment on lines +368 to +385

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Add a null check for sta before calling setPocvQuantile and setPocvMode to prevent a potential crash.

void
pocv_sigma_set_propagate(float per_stage,
                         float n_sigma)
{
  Search::PocvSigma &s = pocvSigmaState();
  s.enabled = true;
  s.per_stage = per_stage;
  s.n_sigma = n_sigma;
  s.propagate = true;
  pocvSyncState();

  ord::OpenRoad *openroad = ord::getOpenRoad();
  sta::dbSta *sta = openroad->getSta();
  if (sta != nullptr) {
    // n_sigma is the sign-off quantile used by DelayOpsNormal::asFloat.
    sta->setPocvQuantile(n_sigma);
    // Statistical readout of the accumulated variance.
    sta->setPocvMode(sta::PocvMode::normal);
  }
}


// OpenROAD-fork: LVF-lib -- does ANY loaded liberty library carry real LVF
// (ocv_sigma_*) delay-sigma tables? Walks libraries -> cells -> combinational
// timing arcs and asks the GateTableModel for a delay sigma table. Used to warn
// the user when -from_liberty is requested but the libs have no LVF data (in
// which case library-driven POCV yields zero variance == baseline).
static bool
anyLibraryHasLvf()
{
ord::OpenRoad *openroad = ord::getOpenRoad();
sta::dbSta *sta = openroad->getSta();
LibertyLibraryIterator *lib_iter = sta->network()->libertyLibraryIterator();
bool found = false;
while (lib_iter->hasNext() && !found) {
LibertyLibrary *lib = lib_iter->next();
LibertyCellIterator cell_iter(lib);
while (cell_iter.hasNext() && !found) {
LibertyCell *cell = cell_iter.next();
for (TimingArcSet *arc_set : cell->timingArcSets()) {
if (arc_set->role() != TimingRole::combinational())
continue;
for (TimingArc *arc : arc_set->arcs()) {
const TimingModel *model = arc->model();
const GateTableModel *gate
= dynamic_cast<const GateTableModel *>(model);
if (gate && gate->delayModels()) {
const TableModels *dm = gate->delayModels();
if (dm->sigma(MinMax::max()) || dm->sigma(MinMax::min())
|| dm->stdDev()) {
found = true;
break;
}
}
}
if (found)
break;
}
}
}
delete lib_iter;
return found;
}
Comment on lines +392 to +427

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Add comprehensive null checks for sta, sta->network(), lib_iter, lib, cell, arc_set, and arc to prevent potential null pointer dereferences and crashes when iterating over loaded libraries.

static bool
anyLibraryHasLvf()
{
  ord::OpenRoad *openroad = ord::getOpenRoad();
  sta::dbSta *sta = openroad->getSta();
  if (sta == nullptr || sta->network() == nullptr) {
    return false;
  }
  LibertyLibraryIterator *lib_iter = sta->network()->libertyLibraryIterator();
  if (lib_iter == nullptr) {
    return false;
  }
  bool found = false;
  while (lib_iter->hasNext() && !found) {
    LibertyLibrary *lib = lib_iter->next();
    if (lib == nullptr) {
      continue;
    }
    LibertyCellIterator cell_iter(lib);
    while (cell_iter.hasNext() && !found) {
      LibertyCell *cell = cell_iter.next();
      if (cell == nullptr) {
        continue;
      }
      for (TimingArcSet *arc_set : cell->timingArcSets()) {
        if (arc_set == nullptr) {
          continue;
        }
        if (arc_set->role() != TimingRole::combinational())
          continue;
        for (TimingArc *arc : arc_set->arcs()) {
          if (arc == nullptr) {
            continue;
          }
          const TimingModel *model = arc->model();
          const GateTableModel *gate
              = dynamic_cast<const GateTableModel *>(model);
          if (gate && gate->delayModels()) {
            const TableModels *dm = gate->delayModels();
            if (dm && (dm->sigma(MinMax::max()) || dm->sigma(MinMax::min())
                || dm->stdDev())) {
              found = true;
              break;
            }
          }
        }
        if (found)
          break;
      }
    }
  }
  delete lib_iter;
  return found;
}


// OpenROAD-fork: LVF-lib -- true if at least one loaded library has LVF tables.
bool
pocv_liberty_has_lvf()
{
return anyLibraryHasLvf();
}

// OpenROAD-fork: LVF-lib -- enable LIBRARY-DRIVEN POCV. The per-stage delay
// sigma comes from the real Liberty LVF ocv_sigma_* tables via the NATIVE
// statistical delay calc (GateTableModel::gateDelayPocv under PocvMode::normal),
// so sigma varies per cell / per arc -- the sign-off-accurate source -- instead
// of one global hand-set number. The synthetic per-stage injection in
// Search::deratedDelayData is suppressed in this mode (PocvSigma::from_liberty)
// so it cannot overwrite the library-derived stdDev. n_sigma is the sign-off
// quantile. Switching the mode invalidates arrivals (Sta::setPocvMode).
void
pocv_sigma_set_from_liberty(float n_sigma)
{
Search::PocvSigma &s = pocvSigmaState();
s.enabled = true;
s.per_stage = 0.0f; // unused in library mode (sigma is per-cell from LVF)
s.n_sigma = n_sigma;
s.propagate = true; // library variance still flows through propagation
s.from_liberty = true; // ... but via native LVF, not synthetic injection
pocvSyncState();

ord::OpenRoad *openroad = ord::getOpenRoad();
sta::dbSta *sta = openroad->getSta();
sta->setPocvQuantile(n_sigma);
sta->setPocvMode(sta::PocvMode::normal);
}
Comment on lines +444 to +459

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Add a null check for sta before calling setPocvQuantile and setPocvMode to prevent a potential crash.

void
pocv_sigma_set_from_liberty(float n_sigma)
{
  Search::PocvSigma &s = pocvSigmaState();
  s.enabled = true;
  s.per_stage = 0.0f;     // unused in library mode (sigma is per-cell from LVF)
  s.n_sigma = n_sigma;
  s.propagate = true;     // library variance still flows through propagation
  s.from_liberty = true;  // ... but via native LVF, not synthetic injection
  pocvSyncState();

  ord::OpenRoad *openroad = ord::getOpenRoad();
  sta::dbSta *sta = openroad->getSta();
  if (sta != nullptr) {
    sta->setPocvQuantile(n_sigma);
    sta->setPocvMode(sta::PocvMode::normal);
  }
}


void
pocv_sigma_clear()
{
const bool was_propagate = pocvSigmaState().propagate;
pocvSigmaState() = Search::PocvSigma(); // back to inactive default
pocvSyncState();
// OpenROAD-fork: LVF -- if propagation mode had been enabled, return the
// timer to scalar delay-ops so timing is byte-identical to baseline again.
if (was_propagate) {
ord::OpenRoad *openroad = ord::getOpenRoad();
sta::dbSta *sta = openroad->getSta();
sta->setPocvMode(sta::PocvMode::scalar);
}
}
Comment on lines +461 to +474

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Add a null check for sta before calling setPocvMode to prevent a potential crash.

void
pocv_sigma_clear()
{
  const bool was_propagate = pocvSigmaState().propagate;
  pocvSigmaState() = Search::PocvSigma();  // back to inactive default
  pocvSyncState();
  // OpenROAD-fork: LVF -- if propagation mode had been enabled, return the
  // timer to scalar delay-ops so timing is byte-identical to baseline again.
  if (was_propagate) {
    ord::OpenRoad *openroad = ord::getOpenRoad();
    sta::dbSta *sta = openroad->getSta();
    if (sta != nullptr) {
      sta->setPocvMode(sta::PocvMode::scalar);
    }
  }
}


bool
pocv_sigma_active()
{
return pocvSigmaState().active();
}

// Recompute the POCV statistical slack for one path end and return a Tcl list:
// {endpoint logic_depth flat_slack pocv_slack flat_sigma rss_sigma n_sigma}
// Slacks and sigmas are converted to the user time unit.
StringSeq
pocv_adjust_path_end(PathEnd *path_end)
{
ord::OpenRoad *openroad = ord::getOpenRoad();
sta::dbSta *sta = openroad->getSta();
PocvPathResult r = pocvAdjustPathEnd(sta, path_end, pocvSigmaState());
sta::Unit *time_unit = sta->units()->timeUnit();
auto fmt = [](double v) {
std::ostringstream ss;
ss.setf(std::ios::fixed);
ss << std::setprecision(9) << v;
return ss.str();
};
StringSeq out;
out.push_back(r.endpoint);
out.push_back(std::to_string(r.logic_depth));
out.push_back(fmt(time_unit->staToUser(r.flat_slack)));
out.push_back(fmt(time_unit->staToUser(r.pocv_slack)));
out.push_back(fmt(time_unit->staToUser(r.flat_sigma)));
out.push_back(fmt(time_unit->staToUser(r.rss_sigma)));
out.push_back(fmt(r.n_sigma));
return out;
}
Comment on lines +485 to +507

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Add null checks for sta, path_end, and time_unit to prevent potential null pointer dereferences and crashes.

StringSeq
pocv_adjust_path_end(PathEnd *path_end)
{
  ord::OpenRoad *openroad = ord::getOpenRoad();
  sta::dbSta *sta = openroad->getSta();
  if (sta == nullptr || path_end == nullptr) {
    return StringSeq();
  }
  PocvPathResult r = pocvAdjustPathEnd(sta, path_end, pocvSigmaState());
  sta::Unit *time_unit = sta->units()->timeUnit();
  if (time_unit == nullptr) {
    return StringSeq();
  }
  auto fmt = [](double v) {
    std::ostringstream ss;
    ss.setf(std::ios::fixed);
    ss << std::setprecision(9) << v;
    return ss.str();
  };
  StringSeq out;
  out.push_back(r.endpoint);
  out.push_back(std::to_string(r.logic_depth));
  out.push_back(fmt(time_unit->staToUser(r.flat_slack)));
  out.push_back(fmt(time_unit->staToUser(r.pocv_slack)));
  out.push_back(fmt(time_unit->staToUser(r.flat_sigma)));
  out.push_back(fmt(time_unit->staToUser(r.rss_sigma)));
  out.push_back(fmt(r.n_sigma));
  return out;
}


// OpenROAD-fork: LVF -- read the PROPAGATION-TIME statistical slack of a path
// end. During propagation-mode POCV the path's arrival carries an accumulated
// variance (sum of (k*d_i)^2). The Tcl `slack`/`arrival` properties expose only
// the MEAN (delayAsFloat single-arg), so this accessor exposes the full
// statistical picture for testing/reporting. Returns a Tcl list:
// {mean_slack slack_std_dev stat_slack n_sigma}
// where stat_slack = mean_slack - n_sigma*slack_std_dev (worst-case readout:
// the slack std-dev combines arrival+required variance, and the statistical
// worst slack subtracts the sigma margin). With POCV inactive / propagate off,
// slack_std_dev == 0 so stat_slack == mean_slack (baseline-safe).
StringSeq
pocv_path_end_stat_slack(PathEnd *path_end)
{
ord::OpenRoad *openroad = ord::getOpenRoad();
sta::dbSta *sta = openroad->getSta();
const sta::Slack slack = path_end->slack(sta);
const float mean = slack.mean();
const float std_dev = slack.stdDev();
const float n_sigma = pocvSigmaState().n_sigma;
// Worst-case statistical slack: pull the slack down by n_sigma sigmas.
const float stat = mean - n_sigma * std_dev;
sta::Unit *time_unit = sta->units()->timeUnit();
auto fmt = [](double v) {
std::ostringstream ss;
ss.setf(std::ios::fixed);
ss << std::setprecision(9) << v;
return ss.str();
};
StringSeq out;
out.push_back(fmt(time_unit->staToUser(mean)));
out.push_back(fmt(time_unit->staToUser(std_dev)));
out.push_back(fmt(time_unit->staToUser(stat)));
out.push_back(fmt(n_sigma));
return out;
}
Comment on lines +519 to +543

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Add null checks for sta, path_end, and time_unit to prevent potential null pointer dereferences and crashes.

StringSeq
pocv_path_end_stat_slack(PathEnd *path_end)
{
  ord::OpenRoad *openroad = ord::getOpenRoad();
  sta::dbSta *sta = openroad->getSta();
  if (sta == nullptr || path_end == nullptr) {
    return StringSeq();
  }
  const sta::Slack slack = path_end->slack(sta);
  const float mean = slack.mean();
  const float std_dev = slack.stdDev();
  const float n_sigma = pocvSigmaState().n_sigma;
  // Worst-case statistical slack: pull the slack down by n_sigma sigmas.
  const float stat = mean - n_sigma * std_dev;
  sta::Unit *time_unit = sta->units()->timeUnit();
  if (time_unit == nullptr) {
    return StringSeq();
  }
  auto fmt = [](double v) {
    std::ostringstream ss;
    ss.setf(std::ios::fixed);
    ss << std::setprecision(9) << v;
    return ss.str();
  };
  StringSeq out;
  out.push_back(fmt(time_unit->staToUser(mean)));
  out.push_back(fmt(time_unit->staToUser(std_dev)));
  out.push_back(fmt(time_unit->staToUser(stat)));
  out.push_back(fmt(n_sigma));
  return out;
}


} // namespace sta

bool
Expand Down
Loading
Loading