Merge pull request #259 from eder-matheus/update_sta

Update sta
This commit is contained in:
Eder Monteiro 2025-01-21 09:10:53 -03:00 committed by GitHub
commit 95826f1eb9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
75 changed files with 2133 additions and 62140 deletions

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
cmake_minimum_required (VERSION 3.9) cmake_minimum_required (VERSION 3.10)
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
# Use standard target names # Use standard target names
cmake_policy(SET CMP0078 NEW) cmake_policy(SET CMP0078 NEW)
@ -125,6 +125,11 @@ set(STA_SOURCE
parasitics/SpefNamespace.cc parasitics/SpefNamespace.cc
parasitics/SpefReader.cc parasitics/SpefReader.cc
parasitics/SpefReaderPvt.hh parasitics/SpefReaderPvt.hh
power/Power.cc
power/VcdReader.cc
power/SaifReader.cc
power/VcdParse.cc
sdc/Clock.cc sdc/Clock.cc
sdc/ClockGatingCheck.cc sdc/ClockGatingCheck.cc
@ -198,11 +203,7 @@ set(STA_SOURCE
spice/WriteSpice.cc spice/WriteSpice.cc
spice/Xyce.cc spice/Xyce.cc
power/Power.cc tcl/TclTypeHelpers.cc
power/ReadVcdActivities.cc
power/SaifReader.cc
power/Vcd.cc
power/VcdReader.cc
util/Debug.cc util/Debug.cc
util/DispatchQueue.cc util/DispatchQueue.cc

View File

@ -11,7 +11,7 @@ RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo \
RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo \ RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo \
&& sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo \ && sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo \
&& sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo \ && sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo \
&& yum install -y devtoolset-8 wget cmake3 make eigen3-devel tcl-devel tcl-tclreadline-devel swig3 bison flex zlib-devel \ && yum install -y devtoolset-8 wget cmake3 make eigen3-devel tcl-devel tcl-tclreadline-devel swig3 bison flex zlib-devel valgrind \
&& yum clean -y all && yum clean -y all
# Download CUDD # Download CUDD

View File

@ -16,7 +16,8 @@ RUN apt-get update && \
bison \ bison \
flex \ flex \
automake \ automake \
autotools-dev autotools-dev \
valgrind
# Download CUDD # Download CUDD
RUN wget https://raw.githubusercontent.com/davidkebo/cudd/main/cudd_versions/cudd-3.0.0.tar.gz && \ RUN wget https://raw.githubusercontent.com/davidkebo/cudd/main/cudd_versions/cudd-3.0.0.tar.gz && \

View File

@ -94,7 +94,7 @@ work, but these are the versions used for development.
cmake 3.24.2 3.29.2 cmake 3.24.2 3.29.2
clang 15.0.0 clang 15.0.0
gcc 11.4.0 gcc 11.4.0
tcl 8.6 8.6.6 tcl 8.6 8.6.16
swig 4.1.0 4.1.1 swig 4.1.0 4.1.1
bison 3.8.2 3.8.2 bison 3.8.2 3.8.2
flex 2.6.4 2.6.4 flex 2.6.4 2.6.4
@ -105,7 +105,7 @@ are illegal in c++17.
External library dependencies: External library dependencies:
``` ```
Ubuntu Macos license Ubuntu Darwin License
eigen 3.4.0 3.4.0 MPL2 required eigen 3.4.0 3.4.0 MPL2 required
cudd 3.0.0 3.0.0 BSD required cudd 3.0.0 3.0.0 BSD required
tclreadline 2.3.8 2.3.8 BSD optional tclreadline 2.3.8 2.3.8 BSD optional
@ -197,7 +197,7 @@ following command builds a Docker image.
``` ```
cd OpenSTA cd OpenSTA
docker build --file Dockerfile.ubuntu_22.04 --tag OpenSTA . docker build --file Dockerfile.ubuntu22.04 --tag OpenSTA .
``` ```
To run a docker container using the OpenSTA image, use the -v option To run a docker container using the OpenSTA image, use the -v option
@ -237,7 +237,22 @@ case directory.
* James Cherry * James Cherry
* William Scott authored the arnoldi delay calculator at Blaze, Inc which was subsequently licensed to Nefelus, Inc that has graciously contributed it to OpenSTA. * William Scott authored the arnoldi delay calculator at Blaze, Inc
which was subsequently licensed to Nefelus, Inc that has graciously
contributed it to OpenSTA.
## Contributions
Contributors must sign the Contributor License Agreement (doc/CLA.txt)
when submitting pull requests.
All contributors should read doc/CodingGuidelines.txt for nodes on
making code that adheres to the existing naming and formatting style.
Contributions that claim 4% performance improvements in OpenROAD flow
scripts will largely be ignored. Small performance improvements
simply do not justify the time requied to audit and verify the changes.
## License ## License

View File

@ -22,7 +22,8 @@
# because there doesn't appear to be a way to override # because there doesn't appear to be a way to override
# searching OSX system directories before unix directories. # searching OSX system directories before unix directories.
set(TCL_POSSIBLE_NAMES tcl87 tcl8.7 set(TCL_POSSIBLE_NAMES
tcl87 tcl8.7
tcl86 tcl8.6 tcl86 tcl8.6
tcl85 tcl8.5 tcl85 tcl8.5
) )
@ -30,7 +31,7 @@ set(TCL_POSSIBLE_NAMES tcl87 tcl8.7
# tcl lib path guesses. # tcl lib path guesses.
if (NOT TCL_LIB_PATHS) if (NOT TCL_LIB_PATHS)
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(TCL_LIB_PATHS /usr/local/lib /opt/homebrew/opt/tcl-tk/lib) set(TCL_LIB_PATHS /opt/homebrew/Cellar/tcl-tk@8/8.6.16/lib /opt/homebrew/opt/tcl-tk/lib /usr/local/lib)
set(TCL_NO_DEFAULT_PATH TRUE) set(TCL_NO_DEFAULT_PATH TRUE)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(TCL_LIB_PATHS /usr/lib /usr/lib64 /usr/local/lib) set(TCL_LIB_PATHS /usr/lib /usr/lib64 /usr/local/lib)

View File

@ -628,7 +628,7 @@ GraphDelayCalc::loadSlewsChanged(DrvrLoadSlews &load_slews_prev,
for (auto const [pin, index] : load_pin_index_map) { for (auto const [pin, index] : load_pin_index_map) {
Vertex *load_vertex = graph_->pinLoadVertex(pin); Vertex *load_vertex = graph_->pinLoadVertex(pin);
const SlewSeq slews = graph_->slews(load_vertex); const SlewSeq slews = graph_->slews(load_vertex);
const SlewSeq &slews_prev = load_slews_prev[index]; const SlewSeq &slews_prev = load_slews_prev[index];
for (size_t i = 0; i < slews.size(); i++) { for (size_t i = 0; i < slews.size(); i++) {
if (!delayEqual(slews[i], slews_prev[i])) if (!delayEqual(slews[i], slews_prev[i]))
return true; return true;
@ -1109,10 +1109,9 @@ GraphDelayCalc::annotateDelaySlew(Edge *edge,
float prev_gate_delay1 = delayAsFloat(prev_gate_delay); float prev_gate_delay1 = delayAsFloat(prev_gate_delay);
if (prev_gate_delay1 == 0.0 if (prev_gate_delay1 == 0.0
|| (abs(gate_delay1 - prev_gate_delay1) / prev_gate_delay1 || (abs(gate_delay1 - prev_gate_delay1) / prev_gate_delay1
> incremental_delay_tolerance_)) { > incremental_delay_tolerance_))
delay_changed = true; delay_changed = true;
graph_->setArcDelay(edge, arc, ap_index, gate_delay); graph_->setArcDelay(edge, arc, ap_index, gate_delay);
}
} }
return delay_changed; return delay_changed;
} }

View File

@ -31,11 +31,14 @@ is now supported by the the read_saif command.
read_saif [-scope scope] filename read_saif [-scope scope] filename
The report_checks -group_count option has been renamed to -group_path_count. The report_checks -group_count option has been renamed to -group_path_count.
The report_checks -endpoing_count option has been renamed to -endpoint_path_count. The report_checks -endpoint_count option has been renamed to -endpoint_path_count.
The report_checks -field hierarchical_pins field reports hierarical pins between The report_checks -field hierarchical_pins field reports hierarchical pins between
a driver and a load in the path report. a driver and a load in the path report.
The suppress_msg and unsuppress_msg commands allow suppression/unsuppression of
warnings/errors by ID. Message IDs can be found in doc/messages.txt.
Release 2.5.0 2024/01/17 Release 2.5.0 2024/01/17
------------------------- -------------------------
@ -62,6 +65,11 @@ clocks from the list.
all_inputs [-no_clocks] all_inputs [-no_clocks]
The report_activity_annotation command reports power activity annotaton
from vcd, saif or the set_input_activity command.
report_activity_annotation [-report_unannotated] [-report_annotated]
Release 2.4.0 2023/01/19 Release 2.4.0 2023/01/19
------------------------- -------------------------

View File

@ -97,15 +97,15 @@ private order.
friend class Frobulator; friend class Frobulator;
} }
Don't declare class variables as const. It means any downstream code
that accesses the member cannot modify it, which is overly
restrictive.
Avoid using [] to lookup a map value because it creates a key/null value Avoid using [] to lookup a map value because it creates a key/null value
pair if the lookup fails. Use map::find or sta::Map::findKey instead. pair if the lookup fails. Use map::find or sta::Map::findKey instead.
Avoid nested classes/enums because SWIG has trouble with them. Avoid nested classes/enums because SWIG has trouble with them.
Avoid all use of global variables as "caches", even if they are thread local.
OpenSTA goes to great lengths to minimize global state variable that prevent
multiple instances of the Sta class from coexisting.
Regression Tests Regression Tests
................ ................

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -2,11 +2,11 @@
read_liberty sky130hd_tt.lib.gz read_liberty sky130hd_tt.lib.gz
read_verilog gcd_sky130hd.v read_verilog gcd_sky130hd.v
link_design gcd link_design gcd
read_sdc gcd_sky130hd.sdc read_sdc gcd_sky130hd.sdc
read_spef gcd_sky130hd.spef read_spef gcd_sky130hd.spef
# Generate vcd file # Generate vcd file
# iverilog -o gcd_tb gcd_tb.v # iverilog -o gcd_tb gcd_tb.v
# vvp gcd_tb # vvp gcd_tb
read_vcd -scope gcd_tb/gcd1 gcd_sky130hd.vcd read_vcd -scope gcd_tb/gcd1 gcd_sky130hd.vcd.gz
report_activity_annotation
report_power report_power

View File

@ -34,11 +34,14 @@ public:
class ExceptionMsg : public Exception class ExceptionMsg : public Exception
{ {
public: public:
ExceptionMsg(const char *msg); ExceptionMsg(const char *msg,
const bool suppressed);
virtual const char *what() const noexcept; virtual const char *what() const noexcept;
virtual bool suppressed() const { return suppressed_; }
private: private:
string msg_; string msg_;
bool suppressed_;
}; };
class ExceptionLine : public Exception class ExceptionLine : public Exception

View File

@ -68,8 +68,7 @@ public:
Path *path() { return &path_; } Path *path() { return &path_; }
const Path *path() const { return &path_; } const Path *path() const { return &path_; }
PathRef &pathRef() { return path_; } PathRef &pathRef() { return path_; }
virtual void setPath(PathEnumed *path, virtual void setPath(const Path *path);
const StaState *sta);
Vertex *vertex(const StaState *sta) const; Vertex *vertex(const StaState *sta) const;
const MinMax *minMax(const StaState *sta) const; const MinMax *minMax(const StaState *sta) const;
// Synonym for minMax(). // Synonym for minMax().
@ -256,8 +255,7 @@ public:
virtual Slack slackNoCrpr(const StaState *sta) const; virtual Slack slackNoCrpr(const StaState *sta) const;
virtual int exceptPathCmp(const PathEnd *path_end, virtual int exceptPathCmp(const PathEnd *path_end,
const StaState *sta) const; const StaState *sta) const;
virtual void setPath(PathEnumed *path, virtual void setPath(const Path *path);
const StaState *sta);
protected: protected:
PathEndClkConstrained(Path *path, PathEndClkConstrained(Path *path,

View File

@ -37,7 +37,7 @@ typedef Map<const char*, PathGroup*, CharPtrLess> PathGroupNamedMap;
class PathGroup class PathGroup
{ {
public: public:
virtual ~PathGroup(); ~PathGroup();
// Path group that compares compare slacks. // Path group that compares compare slacks.
static PathGroup *makePathGroupArrival(const char *name, static PathGroup *makePathGroupArrival(const char *name,
int group_path_count, int group_path_count,
@ -59,8 +59,9 @@ public:
void insert(PathEnd *path_end); void insert(PathEnd *path_end);
// Push group_path_count into path_ends. // Push group_path_count into path_ends.
void pushEnds(PathEndSeq &path_ends); void pushEnds(PathEndSeq &path_ends);
// Predicates to determine if a PathEnd is worth saving. // Predicate to determine if a PathEnd is worth saving.
virtual bool savable(PathEnd *path_end); bool saveable(PathEnd *path_end);
bool enumMinSlackUnderMin(PathEnd *path_end);
int maxPaths() const { return group_path_count_; } int maxPaths() const { return group_path_count_; }
PathGroupIterator *iterator(); PathGroupIterator *iterator();
// This does NOT delete the path ends. // This does NOT delete the path ends.

View File

@ -38,17 +38,17 @@ class PwrActivity
{ {
public: public:
PwrActivity(); PwrActivity();
PwrActivity(float activity, PwrActivity(float density,
float duty, float duty,
PwrActivityOrigin origin); PwrActivityOrigin origin);
float activity() const { return activity_; } float density() const { return density_; }
void setActivity(float activity); void setDensity(float density);
float duty() const { return duty_; } float duty() const { return duty_; }
void setDuty(float duty); void setDuty(float duty);
PwrActivityOrigin origin() { return origin_; } PwrActivityOrigin origin() const { return origin_; }
void setOrigin(PwrActivityOrigin origin); void setOrigin(PwrActivityOrigin origin);
const char *originName() const; const char *originName() const;
void set(float activity, void set(float density,
float duty, float duty,
PwrActivityOrigin origin); PwrActivityOrigin origin);
bool isSet() const; bool isSet() const;
@ -56,12 +56,11 @@ public:
private: private:
void check(); void check();
// In general activity is per clock cycle, NOT per second. float density_; // transitions / second
float activity_; float duty_; // probability signal is high
float duty_;
PwrActivityOrigin origin_; PwrActivityOrigin origin_;
static constexpr float min_activity = 1E-10; static constexpr float min_density = 1E-10;
}; };
class PowerResult class PowerResult
@ -69,12 +68,15 @@ class PowerResult
public: public:
PowerResult(); PowerResult();
void clear(); void clear();
float &internal() { return internal_; } float internal() const { return internal_; }
float &switching() { return switching_; } float switching() const { return switching_; }
float &leakage() { return leakage_; } float leakage() const { return leakage_; }
float total() const; float total() const;
void incr(PowerResult &result); void incr(PowerResult &result);
void incrInternal(float pwr);
void incrSwitching(float pwr);
void incrLeakage(float pwr);
private: private:
float internal_; float internal_;
float switching_; float switching_;

View File

@ -20,6 +20,7 @@
#include <cstdarg> #include <cstdarg>
#include <string> #include <string>
#include <mutex> #include <mutex>
#include <set>
#include "Machine.hh" // __attribute__ #include "Machine.hh" // __attribute__
@ -119,6 +120,11 @@ public:
size_t length); size_t length);
static Report *defaultReport() { return default_; } static Report *defaultReport() { return default_; }
// Suppress message by id.
void suppressMsgId(int id);
void unsuppressMsgId(int id);
bool isSuppressed(int id);
protected: protected:
// All sta print functions have an implicit return printed by this function. // All sta print functions have an implicit return printed by this function.
virtual void printLine(const char *line, virtual void printLine(const char *line,
@ -152,6 +158,7 @@ protected:
size_t buffer_length_; size_t buffer_length_;
std::mutex buffer_lock_; std::mutex buffer_lock_;
static Report *default_; static Report *default_;
std::set<int> suppressed_msg_ids_;
friend class Debug; friend class Debug;
}; };

View File

@ -911,6 +911,7 @@ public:
PathEnd *prev_end, PathEnd *prev_end,
bool last); bool last);
void reportPathEnd(PathEnd *end); void reportPathEnd(PathEnd *end);
void reportPathEnds(PathEndSeq *ends);
ReportPath *reportPath() { return report_path_; } ReportPath *reportPath() { return report_path_; }
void reportPath(Path *path); void reportPath(Path *path);
@ -1291,7 +1292,7 @@ public:
PowerResult &pad); PowerResult &pad);
PowerResult power(const Instance *inst, PowerResult power(const Instance *inst,
const Corner *corner); const Corner *corner);
PwrActivity findClkedActivity(const Pin *pin); PwrActivity activity(const Pin *pin);
void writeTimingModel(const char *lib_name, void writeTimingModel(const char *lib_name,
const char *cell_name, const char *cell_name,

View File

@ -0,0 +1,61 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, 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/>.
#include "ArcDelayCalc.hh"
#include "StringSet.hh"
#include "StringSeq.hh"
#include <tcl.h>
namespace sta {
#if TCL_MAJOR_VERSION < 9
typedef int Tcl_Size;
#endif
StringSet *
tclListSetConstChar(Tcl_Obj *const source,
Tcl_Interp *interp);
StringSeq *
tclListSeqConstChar(Tcl_Obj *const source,
Tcl_Interp *interp);
StdStringSet *
tclListSetStdString(Tcl_Obj *const source,
Tcl_Interp *interp);
void
tclArgError(Tcl_Interp *interp,
const char *msg,
const char *arg);
void
objectListNext(const char *list,
const char *type,
// Return values.
bool &type_match,
const char *&next);
Tcl_Obj *
tclArcDcalcArg(ArcDcalcArg &gate,
Tcl_Interp *interp);
ArcDcalcArg
arcDcalcArgTcl(Tcl_Obj *obj,
Tcl_Interp *interp);
} // namespace

View File

@ -219,7 +219,7 @@ TableTemplate *
LibertyLibrary::findTableTemplate(const char *name, LibertyLibrary::findTableTemplate(const char *name,
TableTemplateType type) TableTemplateType type)
{ {
return template_maps_[int(type)][name]; return template_maps_[int(type)].findKey(name);
} }
TableTemplateSeq TableTemplateSeq

View File

@ -103,6 +103,7 @@ LibertyReader::init(const char *filename,
saved_port_group_ = nullptr; saved_port_group_ = nullptr;
in_bus_ = false; in_bus_ = false;
in_bundle_ = false; in_bundle_ = false;
in_ccsn_ = false;
sequential_ = nullptr; sequential_ = nullptr;
statetable_ = nullptr; statetable_ = nullptr;
timing_ = nullptr; timing_ = nullptr;
@ -545,6 +546,24 @@ LibertyReader::defineVisitors()
defineAttrVisitor("driver_waveform_name", &LibertyReader::visitDriverWaveformName); defineAttrVisitor("driver_waveform_name", &LibertyReader::visitDriverWaveformName);
defineAttrVisitor("driver_waveform_rise", &LibertyReader::visitDriverWaveformRise); defineAttrVisitor("driver_waveform_rise", &LibertyReader::visitDriverWaveformRise);
defineAttrVisitor("driver_waveform_fall", &LibertyReader::visitDriverWaveformFall); defineAttrVisitor("driver_waveform_fall", &LibertyReader::visitDriverWaveformFall);
// ccsn (not implemented, this is needed to properly ignore ccsn groups)
defineGroupVisitor("ccsn_first_stage", &LibertyReader::beginCcsn,
&LibertyReader::endCcsn);
defineGroupVisitor("ccsn_last_stage", &LibertyReader::beginCcsn,
&LibertyReader::endCcsn);
defineGroupVisitor("output_voltage_rise", &LibertyReader::beginCcsn,
&LibertyReader::endCcsn);
defineGroupVisitor("output_voltage_fall", &LibertyReader::beginCcsn,
&LibertyReader::endCcsn);
defineGroupVisitor("propagated_noise_low", &LibertyReader::beginCcsn,
&LibertyReader::endCcsn);
defineGroupVisitor("propagated_noise_high", &LibertyReader::beginCcsn,
&LibertyReader::endCcsn);
defineGroupVisitor("input_ccb", &LibertyReader::beginCcsn,
&LibertyReader::endCcsn);
defineGroupVisitor("output_ccb", &LibertyReader::beginCcsn,
&LibertyReader::endCcsn);
} }
void void
@ -2711,7 +2730,7 @@ LibertyReader::endOutputCurrentRiseFall(LibertyGroup *group)
void void
LibertyReader::beginVector(LibertyGroup *group) LibertyReader::beginVector(LibertyGroup *group)
{ {
if (timing_) { if (timing_ && !in_ccsn_) {
beginTable(group, TableTemplateType::output_current, current_scale_); beginTable(group, TableTemplateType::output_current, current_scale_);
scale_factor_type_ = ScaleFactorType::unknown; scale_factor_type_ = ScaleFactorType::unknown;
reference_time_exists_ = false; reference_time_exists_ = false;
@ -4655,9 +4674,9 @@ LibertyReader::makeTable(LibertyAttr *attr,
float scale) float scale)
{ {
if (attr->isComplex()) { if (attr->isComplex()) {
makeTableAxis(0); makeTableAxis(0, attr);
makeTableAxis(1); makeTableAxis(1, attr);
makeTableAxis(2); makeTableAxis(2, attr);
if (axis_[0] && axis_[1] && axis_[2]) { if (axis_[0] && axis_[1] && axis_[2]) {
// 3D table // 3D table
// Column index1*size(index2) + index2 // Column index1*size(index2) + index2
@ -4682,7 +4701,7 @@ LibertyReader::makeTable(LibertyAttr *attr,
delete table; delete table;
table_ = make_shared<Table1>(values, axis_[0]); table_ = make_shared<Table1>(values, axis_[0]);
} }
else { else if (axis_[0] == nullptr && axis_[1] == nullptr && axis_[2] == nullptr) {
// scalar // scalar
FloatTable *table = makeFloatTable(attr, 1, 1, scale); FloatTable *table = makeFloatTable(attr, 1, 1, scale);
float value = (*(*table)[0])[0]; float value = (*(*table)[0])[0];
@ -4717,20 +4736,18 @@ LibertyReader::makeFloatTable(LibertyAttr *attr,
else else
libWarn(1258, attr, "%s is not a list of floats.", attr->name()); libWarn(1258, attr, "%s is not a list of floats.", attr->name());
if (row->size() != cols) { if (row->size() != cols) {
libWarn(1259, attr, "table row has %u columns but axis has %d.", libWarn(1259, attr, "table row has %zu columns but axis has %zu.",
// size_t is long on 64 bit ports. row->size(),
static_cast<unsigned>(row->size()), cols);
static_cast<unsigned>(cols));
// Fill out row columns with zeros. // Fill out row columns with zeros.
for (size_t c = row->size(); c < cols; c++) for (size_t c = row->size(); c < cols; c++)
row->push_back(0.0); row->push_back(0.0);
} }
} }
if (table->size() != rows) { if (table->size() != rows) {
libWarn(1260, attr, "table has %u rows but axis has %d.", libWarn(1260, attr, "table has %zu rows but axis has %zu.",
// size_t is long on 64 bit ports. table->size(),
static_cast<unsigned>(table->size()), rows);
static_cast<unsigned>(rows));
// Fill with zero'd rows. // Fill with zero'd rows.
for (size_t r = table->size(); r < rows; r++) { for (size_t r = table->size(); r < rows; r++) {
FloatSeq *row = new FloatSeq; FloatSeq *row = new FloatSeq;
@ -4744,7 +4761,8 @@ LibertyReader::makeFloatTable(LibertyAttr *attr,
} }
void void
LibertyReader::makeTableAxis(int index) LibertyReader::makeTableAxis(int index,
LibertyAttr *attr)
{ {
if (axis_values_[index]) { if (axis_values_[index]) {
TableAxisVariable var = axis_[index]->variable(); TableAxisVariable var = axis_[index]->variable();
@ -4754,6 +4772,11 @@ LibertyReader::makeTableAxis(int index)
scaleFloats(values, scale); scaleFloats(values, scale);
axis_[index] = make_shared<TableAxis>(var, values); axis_[index] = make_shared<TableAxis>(var, values);
} }
else if (axis_[index] && axis_[index]->values() == nullptr) {
libWarn(1344, attr, "Table axis and template missing values.");
axis_[index] = nullptr;
axis_values_[index] = nullptr;
}
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////

View File

@ -482,6 +482,10 @@ public:
void visitDriverWaveformRiseFall(LibertyAttr *attr, void visitDriverWaveformRiseFall(LibertyAttr *attr,
const RiseFall *rf); const RiseFall *rf);
// ccsn (not implemented, this is needed to properly ignore ccsn groups)
void beginCcsn(LibertyGroup *) { in_ccsn_ = true; }
void endCcsn(LibertyGroup *) { in_ccsn_ = false; }
// Visitors for derived classes to overload. // Visitors for derived classes to overload.
virtual void beginGroup1(LibertyGroup *) {} virtual void beginGroup1(LibertyGroup *) {}
virtual void beginGroup2(LibertyGroup *) {} virtual void beginGroup2(LibertyGroup *) {}
@ -520,7 +524,8 @@ protected:
LibraryAttrVisitor visitor); LibraryAttrVisitor visitor);
void parseNames(const char *name_str); void parseNames(const char *name_str);
void clearAxisValues(); void clearAxisValues();
void makeTableAxis(int index); void makeTableAxis(int index,
LibertyAttr *attr);
StringSeq *parseNameList(const char *name_list); StringSeq *parseNameList(const char *name_list);
StdStringSeq parseTokenList(const char *token_str, StdStringSeq parseTokenList(const char *token_str,
@ -629,6 +634,7 @@ protected:
StringSeq bus_names_; StringSeq bus_names_;
bool in_bus_; bool in_bus_;
bool in_bundle_; bool in_bundle_;
bool in_ccsn_;
TableAxisVariable axis_var_[3]; TableAxisVariable axis_var_[3];
FloatSeq *axis_values_[3]; FloatSeq *axis_values_[3];
int type_bit_from_; int type_bit_from_;

View File

@ -78,7 +78,7 @@ proc_redirect read_spef {
$coupling_reduction_factor $reduce] $coupling_reduction_factor $reduce]
} }
define_cmd_args "report_parasitic_annotation" {-report_unannotated} define_cmd_args "report_parasitic_annotation" {[-report_unannotated]}
proc_redirect report_parasitic_annotation { proc_redirect report_parasitic_annotation {
parse_key_args "report_parasitic_annotation" args \ parse_key_args "report_parasitic_annotation" args \

View File

@ -59,13 +59,12 @@
// input_voltage : default_VDD_VSS_input; // input_voltage : default_VDD_VSS_input;
// pin // pin
// output_voltage : default_VDD_VSS_output; // output_voltage : default_VDD_VSS_output;
//
// transition_density = activity / clock_period
namespace sta { namespace sta {
using std::abs; using std::abs;
using std::max; using std::max;
using std::min;
using std::isnormal; using std::isnormal;
static bool static bool
@ -87,8 +86,8 @@ static EnumNameMap<PwrActivityOrigin> pwr_activity_origin_map =
Power::Power(StaState *sta) : Power::Power(StaState *sta) :
StaState(sta), StaState(sta),
global_activity_{0.0, 0.0, PwrActivityOrigin::unknown}, global_activity_(),
input_activity_{0.1, 0.5, PwrActivityOrigin::input}, input_activity_(), // default set in ensureActivities()
seq_activity_map_(100, SeqPinHash(network_), SeqPinEqual()), seq_activity_map_(100, SeqPinHash(network_), SeqPinEqual()),
activities_valid_(false), activities_valid_(false),
bdd_(sta) bdd_(sta)
@ -96,41 +95,41 @@ Power::Power(StaState *sta) :
} }
void void
Power::setGlobalActivity(float activity, Power::setGlobalActivity(float density,
float duty) float duty)
{ {
global_activity_.set(activity, duty, PwrActivityOrigin::global); global_activity_.set(density, duty, PwrActivityOrigin::global);
activities_valid_ = false; activities_valid_ = false;
} }
void void
Power::setInputActivity(float activity, Power::setInputActivity(float density,
float duty) float duty)
{ {
input_activity_.set(activity, duty, PwrActivityOrigin::input); input_activity_.set(density, duty, PwrActivityOrigin::input);
activities_valid_ = false; activities_valid_ = false;
} }
void void
Power::setInputPortActivity(const Port *input_port, Power::setInputPortActivity(const Port *input_port,
float activity, float density,
float duty) float duty)
{ {
Instance *top_inst = network_->topInstance(); Instance *top_inst = network_->topInstance();
const Pin *pin = network_->findPin(top_inst, input_port); const Pin *pin = network_->findPin(top_inst, input_port);
if (pin) { if (pin) {
user_activity_map_[pin] = {activity, duty, PwrActivityOrigin::user}; user_activity_map_[pin] = {density, duty, PwrActivityOrigin::user};
activities_valid_ = false; activities_valid_ = false;
} }
} }
void void
Power::setUserActivity(const Pin *pin, Power::setUserActivity(const Pin *pin,
float activity, float density,
float duty, float duty,
PwrActivityOrigin origin) PwrActivityOrigin origin)
{ {
user_activity_map_[pin] = {activity, duty, origin}; user_activity_map_[pin] = {density, duty, origin};
activities_valid_ = false; activities_valid_ = false;
} }
@ -152,7 +151,7 @@ Power::setActivity(const Pin *pin,
{ {
debugPrint(debug_, "power_activity", 3, "set %s %.2e %.2f %s", debugPrint(debug_, "power_activity", 3, "set %s %.2e %.2f %s",
network_->pathName(pin), network_->pathName(pin),
activity.activity(), activity.density(),
activity.duty(), activity.duty(),
pwr_activity_origin_map.find(activity.origin())); pwr_activity_origin_map.find(activity.origin()));
activity_map_[pin] = activity; activity_map_[pin] = activity;
@ -403,7 +402,7 @@ PropActivityVisitor::visit(Vertex *vertex)
Vertex *from_vertex = edge->from(graph_); Vertex *from_vertex = edge->from(graph_);
const Pin *from_pin = from_vertex->pin(); const Pin *from_pin = from_vertex->pin();
PwrActivity &from_activity = power_->activity(from_pin); PwrActivity &from_activity = power_->activity(from_pin);
PwrActivity to_activity(from_activity.activity(), PwrActivity to_activity(from_activity.density(),
from_activity.duty(), from_activity.duty(),
PwrActivityOrigin::propagated); PwrActivityOrigin::propagated);
changed = setActivityCheck(pin, to_activity); changed = setActivityCheck(pin, to_activity);
@ -426,13 +425,13 @@ PropActivityVisitor::visit(Vertex *vertex)
PwrActivity activity2 = power_->findActivity(enable); PwrActivity activity2 = power_->findActivity(enable);
float p1 = activity1.duty(); float p1 = activity1.duty();
float p2 = activity2.duty(); float p2 = activity2.duty();
PwrActivity activity(activity1.activity() * p2 + activity2.activity() * p1, PwrActivity activity(activity1.density() * p2 + activity2.density() * p1,
p1 * p2, p1 * p2,
PwrActivityOrigin::propagated); PwrActivityOrigin::propagated);
changed = setActivityCheck(gclk, activity); changed = setActivityCheck(gclk, activity);
debugPrint(debug_, "power_activity", 3, "gated_clk %s %.2e %.2f", debugPrint(debug_, "power_activity", 3, "gated_clk %s %.2e %.2f",
network_->pathName(gclk), network_->pathName(gclk),
activity.activity(), activity.density(),
activity.duty()); activity.duty());
} }
} }
@ -466,13 +465,17 @@ bool
PropActivityVisitor::setActivityCheck(const Pin *pin, PropActivityVisitor::setActivityCheck(const Pin *pin,
PwrActivity &activity) PwrActivity &activity)
{ {
float min_rf_slew = power_->getMinRfSlew(pin);
float max_density = (min_rf_slew > 0.0) ? 1.0 / min_rf_slew : INF;
if (activity.density() > max_density)
activity.setDensity(max_density);
PwrActivity &prev_activity = power_->activity(pin); PwrActivity &prev_activity = power_->activity(pin);
float activity_delta = abs(activity.activity() - prev_activity.activity()); float density_delta = abs(activity.density() - prev_activity.density());
float duty_delta = abs(activity.duty() - prev_activity.duty()); float duty_delta = abs(activity.duty() - prev_activity.duty());
if (activity_delta > change_tolerance_ if (density_delta > change_tolerance_
|| duty_delta > change_tolerance_ || duty_delta > change_tolerance_
|| activity.origin() != prev_activity.origin()) { || activity.origin() != prev_activity.origin()) {
max_change_ = max(max_change_, activity_delta); max_change_ = max(max_change_, density_delta);
max_change_ = max(max_change_, duty_delta); max_change_ = max(max_change_, duty_delta);
power_->setActivity(pin, activity); power_->setActivity(pin, activity);
return true; return true;
@ -517,11 +520,11 @@ Power::evalActivity(FuncExpr *expr,
else { else {
DdNode *bdd = bdd_.funcBdd(expr); DdNode *bdd = bdd_.funcBdd(expr);
float duty = evalBddDuty(bdd, inst); float duty = evalBddDuty(bdd, inst);
float activity = evalBddActivity(bdd, inst); float density = evalBddActivity(bdd, inst);
Cudd_RecursiveDeref(bdd_.cuddMgr(), bdd); Cudd_RecursiveDeref(bdd_.cuddMgr(), bdd);
bdd_.clearVarMap(); bdd_.clearVarMap();
return PwrActivity(activity, duty, PwrActivityOrigin::propagated); return PwrActivity(density, duty, PwrActivityOrigin::propagated);
} }
} }
@ -588,7 +591,7 @@ float
Power::evalBddActivity(DdNode *bdd, Power::evalBddActivity(DdNode *bdd,
const Instance *inst) const Instance *inst)
{ {
float activity = 0.0; float density = 0.0;
for (const auto [port, var_node] : bdd_.portVarMap()) { for (const auto [port, var_node] : bdd_.portVarMap()) {
const Pin *pin = findLinkPin(inst, port); const Pin *pin = findLinkPin(inst, port);
if (pin) { if (pin) {
@ -598,21 +601,16 @@ Power::evalBddActivity(DdNode *bdd,
Cudd_Ref(diff); Cudd_Ref(diff);
float diff_duty = evalBddDuty(diff, inst); float diff_duty = evalBddDuty(diff, inst);
Cudd_RecursiveDeref(bdd_.cuddMgr(), diff); Cudd_RecursiveDeref(bdd_.cuddMgr(), diff);
float var_act = var_activity.activity() * diff_duty; float var_density = var_activity.density() * diff_duty;
activity += var_act; density += var_density;
if (debug_->check("power_activity", 3)) { debugPrint(debug_, "power_activity", 3, "var %s %.3e * %.3f = %.3e",
const Clock *clk = findClk(pin); port->name(),
float clk_period = clk ? clk->period() : 1.0; var_activity.density(),
debugPrint(debug_, "power_activity", 3, "var %s%s %.3e * %.3f = %.3e", diff_duty,
port->name(), var_density);
clk ? "" : " (unclocked)",
var_activity.activity() / clk_period,
diff_duty,
var_act / clk_period);
}
} }
} }
return activity; return density;
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
@ -628,6 +626,13 @@ Power::ensureActivities()
activity_map_.clear(); activity_map_.clear();
seq_activity_map_.clear(); seq_activity_map_.clear();
// Initialize default input activity (after sdc is defined)
// unless it has been set by command.
if (input_activity_.density() == 0.0) {
float min_period = clockMinPeriod();
float density = 0.1 / (min_period != 0.0 ? min_period : 0.0);
input_activity_.set(density, 0.5, PwrActivityOrigin::input);
}
ActivitySrchPred activity_srch_pred(this); ActivitySrchPred activity_srch_pred(this);
BfsFwdIterator bfs(BfsIndex::other, &activity_srch_pred, this); BfsFwdIterator bfs(BfsIndex::other, &activity_srch_pred, this);
seedActivities(bfs); seedActivities(bfs);
@ -722,9 +727,17 @@ Power::seedRegOutputActivities(const Instance *reg,
PwrActivity activity = evalActivity(seq->data(), reg); PwrActivity activity = evalActivity(seq->data(), reg);
// Register output activity cannnot exceed one transition per clock cycle, // Register output activity cannnot exceed one transition per clock cycle,
// but latch output can. // but latch output can.
if (seq->isRegister() if (seq->isRegister()) {
&& activity.activity() > 1.0) FuncExpr *clk_func = seq->clock();
activity.setActivity(1.0); if (clk_func->port()) {
const Pin *pin = network_->findPin(reg, clk_func->port());
const Clock *clk = findClk(pin);
if (clk) {
if (activity.density() > 1.0 / clk->period())
activity.setDensity(1.0 / clk->period());
}
}
}
if (invert) if (invert)
activity.setDuty(1.0 - activity.duty()); activity.setDuty(1.0 - activity.duty());
activity.setOrigin(PwrActivityOrigin::propagated); activity.setOrigin(PwrActivityOrigin::propagated);
@ -740,9 +753,8 @@ Power::power(const Instance *inst,
const Corner *corner) const Corner *corner)
{ {
PowerResult result; PowerResult result;
const Clock *inst_clk = findInstClk(inst); findInternalPower(inst, cell, corner, result);
findInternalPower(inst, cell, corner, inst_clk, result); findSwitchingPower(inst, cell, corner, result);
findSwitchingPower(inst, cell, corner, inst_clk, result);
findLeakagePower(inst, cell, corner, result); findLeakagePower(inst, cell, corner, result);
return result; return result;
} }
@ -768,7 +780,6 @@ void
Power::findInternalPower(const Instance *inst, Power::findInternalPower(const Instance *inst,
LibertyCell *cell, LibertyCell *cell,
const Corner *corner, const Corner *corner,
const Clock *inst_clk,
// Return values. // Return values.
PowerResult &result) PowerResult &result)
{ {
@ -781,7 +792,7 @@ Power::findInternalPower(const Instance *inst,
float load_cap = to_port->direction()->isAnyOutput() float load_cap = to_port->direction()->isAnyOutput()
? graph_delay_calc_->loadCap(to_pin, dcalc_ap) ? graph_delay_calc_->loadCap(to_pin, dcalc_ap)
: 0.0; : 0.0;
PwrActivity activity = findClkedActivity(to_pin, inst_clk); PwrActivity activity = findActivity(to_pin);
if (to_port->direction()->isAnyOutput()) if (to_port->direction()->isAnyOutput())
findOutputInternalPower(to_port, inst, cell, activity, findOutputInternalPower(to_port, inst, cell, activity,
load_cap, corner, result); load_cap, corner, result);
@ -850,18 +861,18 @@ Power::findInputInternalPower(const Pin *pin,
else else
duty = evalActivity(when, inst).duty(); duty = evalActivity(when, inst).duty();
} }
float port_internal = energy * duty * activity.activity(); float port_internal = energy * duty * activity.density();
debugPrint(debug_, "power", 2, " %3s %6s %.2f %.2f %9.2e %9.2e %s", debugPrint(debug_, "power", 2, " %3s %6s %.2f %.2f %9.2e %9.2e %s",
port->name(), port->name(),
when ? when->asString() : "", when ? when->asString() : "",
activity.activity() * 1e-9, activity.density() * 1e-9,
duty, duty,
energy, energy,
port_internal, port_internal,
related_pg_pin ? related_pg_pin : "no pg_pin"); related_pg_pin ? related_pg_pin : "no pg_pin");
internal += port_internal; internal += port_internal;
} }
result.internal() += internal; result.incrInternal(internal);
} }
} }
} }
@ -879,6 +890,27 @@ Power::getSlew(Vertex *vertex,
return delayAsFloat(graph_->slew(vertex, rf, dcalc_ap->index())); return delayAsFloat(graph_->slew(vertex, rf, dcalc_ap->index()));
} }
float
Power::getMinRfSlew(const Pin *pin)
{
Vertex *vertex, *bidir_vertex;
graph_->pinVertices(pin, vertex, bidir_vertex);
if (vertex) {
const MinMax *min_max = MinMax::min();
Slew mm_slew = min_max->initValue();
for (const DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) {
DcalcAPIndex ap_index = dcalc_ap->index();
const Slew &slew1 = graph_->slew(vertex, RiseFall::rise(), ap_index);
const Slew &slew2 = graph_->slew(vertex, RiseFall::fall(), ap_index);
Slew slew = delayAsFloat(slew1 + slew2) / 2.0;
if (delayGreater(slew, mm_slew, min_max, this))
mm_slew = slew;
}
return mm_slew;
}
return 0.0;
}
LibertyPort * LibertyPort *
Power::findExprOutPort(FuncExpr *expr) Power::findExprOutPort(FuncExpr *expr)
{ {
@ -936,11 +968,11 @@ Power::findOutputInternalPower(const LibertyPort *to_port,
const LibertyPort *from_corner_port = pwr->relatedPort(); const LibertyPort *from_corner_port = pwr->relatedPort();
if (from_corner_port) { if (from_corner_port) {
const Pin *from_pin = findLinkPin(inst, from_corner_port); const Pin *from_pin = findLinkPin(inst, from_corner_port);
float from_activity = findActivity(from_pin).activity(); float from_density = findActivity(from_pin).density();
float duty = findInputDuty(inst, func, pwr); float duty = findInputDuty(inst, func, pwr);
const char *related_pg_pin = pwr->relatedPgPin(); const char *related_pg_pin = pwr->relatedPgPin();
// Note related_pg_pin may be null. // Note related_pg_pin may be null.
pg_duty_sum[related_pg_pin] += from_activity * duty; pg_duty_sum[related_pg_pin] += from_density * duty;
} }
} }
@ -982,16 +1014,16 @@ Power::findOutputInternalPower(const LibertyPort *to_port,
if (duty_sum_iter != pg_duty_sum.end()) { if (duty_sum_iter != pg_duty_sum.end()) {
float duty_sum = duty_sum_iter->second; float duty_sum = duty_sum_iter->second;
if (duty_sum != 0.0 && from_pin) { if (duty_sum != 0.0 && from_pin) {
float from_activity = findActivity(from_pin).activity(); float from_density = findActivity(from_pin).density();
weight = from_activity * duty / duty_sum; weight = from_density * duty / duty_sum;
} }
} }
float port_internal = weight * energy * to_activity.activity(); float port_internal = weight * energy * to_activity.density();
debugPrint(debug_, "power", 2, "%3s -> %-3s %6s %.3f %.3f %.3f %9.2e %9.2e %s", debugPrint(debug_, "power", 2, "%3s -> %-3s %6s %.3f %.3f %.3f %9.2e %9.2e %s",
from_corner_port ? from_corner_port->name() : "-" , from_corner_port ? from_corner_port->name() : "-" ,
to_port->name(), to_port->name(),
when ? when->asString() : "", when ? when->asString() : "",
to_activity.activity() * 1e-9, to_activity.density() * 1e-9,
duty, duty,
weight, weight,
energy, energy,
@ -999,7 +1031,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port,
related_pg_pin ? related_pg_pin : "no pg_pin"); related_pg_pin ? related_pg_pin : "no pg_pin");
internal += port_internal; internal += port_internal;
} }
result.internal() += internal; result.incrInternal(internal);
} }
float float
@ -1023,7 +1055,7 @@ Power::findInputDuty(const Instance *inst,
else if (when) else if (when)
return evalActivity(when, inst).duty(); return evalActivity(when, inst).duty();
else if (search_->isClock(from_vertex)) else if (search_->isClock(from_vertex))
return 1.0; return 0.5;
return 0.5; return 0.5;
} }
} }
@ -1068,7 +1100,6 @@ void
Power::findSwitchingPower(const Instance *inst, Power::findSwitchingPower(const Instance *inst,
LibertyCell *cell, LibertyCell *cell,
const Corner *corner, const Corner *corner,
const Clock *inst_clk,
// Return values. // Return values.
PowerResult &result) PowerResult &result)
{ {
@ -1082,17 +1113,17 @@ Power::findSwitchingPower(const Instance *inst,
float load_cap = to_port->direction()->isAnyOutput() float load_cap = to_port->direction()->isAnyOutput()
? graph_delay_calc_->loadCap(to_pin, dcalc_ap) ? graph_delay_calc_->loadCap(to_pin, dcalc_ap)
: 0.0; : 0.0;
PwrActivity activity = findClkedActivity(to_pin, inst_clk); PwrActivity activity = findActivity(to_pin);
if (to_port->direction()->isAnyOutput()) { if (to_port->direction()->isAnyOutput()) {
float volt = portVoltage(corner_cell, to_port, dcalc_ap); float volt = portVoltage(corner_cell, to_port, dcalc_ap);
float switching = .5 * load_cap * volt * volt * activity.activity(); float switching = .5 * load_cap * volt * volt * activity.density();
debugPrint(debug_, "power", 2, "switching %s/%s activity = %.2e volt = %.2f %.3e", debugPrint(debug_, "power", 2, "switching %s/%s activity = %.2e volt = %.2f %.3e",
cell->name(), cell->name(),
to_port->name(), to_port->name(),
activity.activity(), activity.density(),
volt, volt,
switching); switching);
result.switching() += switching; result.incrSwitching(switching);
} }
} }
} }
@ -1160,34 +1191,15 @@ Power::findLeakagePower(const Instance *inst,
debugPrint(debug_, "power", 2, "leakage %s %.3e", debugPrint(debug_, "power", 2, "leakage %s %.3e",
cell->name(), cell->name(),
leakage); leakage);
result.leakage() += leakage; result.incrLeakage(leakage);
} }
// External.
PwrActivity PwrActivity
Power::findClkedActivity(const Pin *pin) Power::pinActivity(const Pin *pin)
{ {
const Instance *inst = network_->instance(pin);
const Clock *inst_clk = findInstClk(inst);
ensureActivities(); ensureActivities();
return findClkedActivity(pin, inst_clk); return findActivity(pin);
}
PwrActivity
Power::findClkedActivity(const Pin *pin,
const Clock *inst_clk)
{
PwrActivity activity = findActivity(pin);
const Clock *clk = findClk(pin);
if (clk == nullptr)
clk = inst_clk;
if (clk) {
float period = clk->period();
if (period > 0.0)
return PwrActivity(activity.activity() / period,
activity.duty(),
activity.origin());
}
return activity;
} }
PwrActivity PwrActivity
@ -1204,7 +1216,7 @@ Power::findActivity(const Pin *pin)
} }
const Clock *clk = findClk(pin); const Clock *clk = findClk(pin);
float duty = clockDuty(clk); float duty = clockDuty(clk);
return PwrActivity(2.0, duty, PwrActivityOrigin::clock); return PwrActivity(2.0 / clk->period(), duty, PwrActivityOrigin::clock);
} }
else if (global_activity_.isSet()) else if (global_activity_.isSet())
return global_activity_; return global_activity_;
@ -1243,10 +1255,9 @@ Power::findSeqActivity(const Instance *inst,
return global_activity_; return global_activity_;
else if (hasSeqActivity(inst, port)) { else if (hasSeqActivity(inst, port)) {
PwrActivity &activity = seqActivity(inst, port); PwrActivity &activity = seqActivity(inst, port);
if (activity.origin() != PwrActivityOrigin::unknown) return activity;
return activity;
} }
return PwrActivity(0.0, 0.0, PwrActivityOrigin::unknown); return PwrActivity();
} }
float float
@ -1305,6 +1316,131 @@ Power::findClk(const Pin *to_pin)
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
void
Power::reportActivityAnnotation(bool report_unannotated,
bool report_annotated)
{
size_t vcd_count = 0;
size_t saif_count = 0;
size_t input_count = 0;
for (auto const& [pin, activity] : user_activity_map_) {
PwrActivityOrigin origin = activity.origin();
switch (origin) {
case PwrActivityOrigin::vcd:
vcd_count++;
break;
case PwrActivityOrigin::saif:
saif_count++;
break;
case PwrActivityOrigin::user:
input_count++;
break;
default:
break;
}
}
if (vcd_count > 0)
report_->reportLine("vcd %5zu", vcd_count);
if (saif_count > 0)
report_->reportLine("saif %5zu", saif_count);
if (input_count > 0)
report_->reportLine("input %5zu", input_count);
size_t pin_count = pinCount();
size_t unannotated_count = pin_count - vcd_count - saif_count - input_count;
report_->reportLine("unannotated %5zu", unannotated_count);
if (report_annotated) {
PinSeq annotated_pins;
for (auto const& [pin, activity] : user_activity_map_)
annotated_pins.push_back(pin);
sort(annotated_pins.begin(), annotated_pins.end(), PinPathNameLess(sdc_network_));
report_->reportLine("Annotated pins:");
for (const Pin *pin : annotated_pins) {
const PwrActivity &activity = user_activity_map_[pin];
PwrActivityOrigin origin = activity.origin();
const char *origin_name = pwr_activity_origin_map.find(origin);
report_->reportLine("%5s %s",
origin_name,
sdc_network_->pathName(pin));
}
}
if (report_unannotated) {
PinSeq unannotated_pins;
findUnannotatedPins(network_->topInstance(), unannotated_pins);
LeafInstanceIterator *inst_iter = network_->leafInstanceIterator();
while (inst_iter->hasNext()) {
const Instance *inst = inst_iter->next();
findUnannotatedPins(inst, unannotated_pins);
}
delete inst_iter;
sort(unannotated_pins.begin(), unannotated_pins.end(),
PinPathNameLess(sdc_network_));
report_->reportLine("Unannotated pins:");
for (const Pin *pin : unannotated_pins) {
report_->reportLine(" %s", sdc_network_->pathName(pin));
}
}
}
void
Power::findUnannotatedPins(const Instance *inst,
PinSeq &unannotated_pins)
{
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
const Pin *pin = pin_iter->next();
if (!network_->direction(pin)->isInternal()
&& user_activity_map_.find(pin) == user_activity_map_.end())
unannotated_pins.push_back(pin);
}
delete pin_iter;
}
// leaf pins - internal pins + top instance pins
size_t
Power::pinCount()
{
size_t count = 0;
LeafInstanceIterator *leaf_iter = network_->leafInstanceIterator();
while (leaf_iter->hasNext()) {
Instance *leaf = leaf_iter->next();
InstancePinIterator *pin_iter = network_->pinIterator(leaf);
while (pin_iter->hasNext()) {
const Pin *pin = pin_iter->next();
if (!network_->direction(pin)->isInternal())
count++;
}
delete pin_iter;
}
delete leaf_iter;
InstancePinIterator *pin_iter = network_->pinIterator(network_->topInstance());
while (pin_iter->hasNext()) {
pin_iter->next();
count++;
}
delete pin_iter;
return count;
}
float
Power::clockMinPeriod()
{
ClockSeq *clks = sdc_->clocks();
if (clks && !clks->empty()) {
float min_period = INF;
for (const Clock *clk : *clks)
min_period = min(min_period, clk->period());
return min_period;
}
else
return 0.0;
}
////////////////////////////////////////////////////////////////
PowerResult::PowerResult() : PowerResult::PowerResult() :
internal_(0.0), internal_(0.0),
switching_(0.0), switching_(0.0),
@ -1326,6 +1462,24 @@ PowerResult::total() const
return internal_ + switching_ + leakage_; return internal_ + switching_ + leakage_;
} }
void
PowerResult::incrInternal(float pwr)
{
internal_ += pwr;
}
void
PowerResult::incrSwitching(float pwr)
{
switching_ += pwr;
}
void
PowerResult::incrLeakage(float pwr)
{
leakage_ += pwr;
}
void void
PowerResult::incr(PowerResult &result) PowerResult::incr(PowerResult &result)
{ {
@ -1336,17 +1490,17 @@ PowerResult::incr(PowerResult &result)
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
PwrActivity::PwrActivity(float activity, PwrActivity::PwrActivity(float density,
float duty, float duty,
PwrActivityOrigin origin) : PwrActivityOrigin origin) :
activity_(activity), density_(density),
duty_(duty), duty_(duty),
origin_(origin) origin_(origin)
{ {
} }
PwrActivity::PwrActivity() : PwrActivity::PwrActivity() :
activity_(0.0), density_(0.0),
duty_(0.0), duty_(0.0),
origin_(PwrActivityOrigin::unknown) origin_(PwrActivityOrigin::unknown)
{ {
@ -1354,9 +1508,9 @@ PwrActivity::PwrActivity() :
} }
void void
PwrActivity::setActivity(float activity) PwrActivity::setDensity(float density)
{ {
activity_ = activity; density_ = density;
} }
void void
@ -1372,11 +1526,11 @@ PwrActivity::setOrigin(PwrActivityOrigin origin)
} }
void void
PwrActivity::set(float activity, PwrActivity::set(float density,
float duty, float duty,
PwrActivityOrigin origin) PwrActivityOrigin origin)
{ {
activity_ = activity; density_ = density;
duty_ = duty; duty_ = duty;
origin_ = origin; origin_ = origin;
check(); check();
@ -1385,11 +1539,11 @@ PwrActivity::set(float activity,
void void
PwrActivity::check() PwrActivity::check()
{ {
// Activities can get very small from multiplying probabilities // Densities can get very small from multiplying probabilities
// through deep chains of logic. Clip them to prevent floating // through deep chains of logic. Clip them to prevent floating
// point anomalies. // point anomalies.
if (abs(activity_) < min_activity) if (abs(density_) < min_density)
activity_ = 0.0; density_ = 0.0;
} }
bool bool

View File

@ -84,15 +84,17 @@ public:
void setInputPortActivity(const Port *input_port, void setInputPortActivity(const Port *input_port,
float activity, float activity,
float duty); float duty);
PwrActivity &activity(const Pin *pin); PwrActivity pinActivity(const Pin *pin);
void setUserActivity(const Pin *pin, void setUserActivity(const Pin *pin,
float activity, float activity,
float duty, float duty,
PwrActivityOrigin origin); PwrActivityOrigin origin);
// Activity is toggles per second. void reportActivityAnnotation(bool report_unannotated,
PwrActivity findClkedActivity(const Pin *pin); bool report_annotated);
float clockMinPeriod();
protected: protected:
PwrActivity &activity(const Pin *pin);
bool inClockNetwork(const Instance *inst); bool inClockNetwork(const Instance *inst);
void powerInside(const Instance *hinst, void powerInside(const Instance *hinst,
const Corner *corner, const Corner *corner,
@ -110,6 +112,7 @@ protected:
bool hasActivity(const Pin *pin); bool hasActivity(const Pin *pin);
void setActivity(const Pin *pin, void setActivity(const Pin *pin,
PwrActivity &activity); PwrActivity &activity);
PwrActivity findActivity(const Pin *pin);
PowerResult power(const Instance *inst, PowerResult power(const Instance *inst,
LibertyCell *cell, LibertyCell *cell,
@ -117,7 +120,6 @@ protected:
void findInternalPower(const Instance *inst, void findInternalPower(const Instance *inst,
LibertyCell *cell, LibertyCell *cell,
const Corner *corner, const Corner *corner,
const Clock *inst_clk,
// Return values. // Return values.
PowerResult &result); PowerResult &result);
void findInputInternalPower(const Pin *to_pin, void findInputInternalPower(const Pin *to_pin,
@ -145,18 +147,15 @@ protected:
void findSwitchingPower(const Instance *inst, void findSwitchingPower(const Instance *inst,
LibertyCell *cell, LibertyCell *cell,
const Corner *corner, const Corner *corner,
const Clock *inst_clk,
// Return values. // Return values.
PowerResult &result); PowerResult &result);
float getSlew(Vertex *vertex, float getSlew(Vertex *vertex,
const RiseFall *rf, const RiseFall *rf,
const Corner *corner); const Corner *corner);
float getMinRfSlew(const Pin *pin);
const Clock *findInstClk(const Instance *inst); const Clock *findInstClk(const Instance *inst);
const Clock *findClk(const Pin *to_pin); const Clock *findClk(const Pin *to_pin);
float clockDuty(const Clock *clk); float clockDuty(const Clock *clk);
PwrActivity findClkedActivity(const Pin *pin,
const Clock *inst_clk);
PwrActivity findActivity(const Pin *pin);
PwrActivity findSeqActivity(const Instance *inst, PwrActivity findSeqActivity(const Instance *inst,
LibertyPort *port); LibertyPort *port);
float portVoltage(LibertyCell *cell, float portVoltage(LibertyCell *cell,
@ -198,6 +197,9 @@ protected:
const Instance *inst); const Instance *inst);
float evalBddDuty(DdNode *bdd, float evalBddDuty(DdNode *bdd,
const Instance *inst); const Instance *inst);
void findUnannotatedPins(const Instance *inst,
PinSeq &unannotated_pins);
size_t pinCount();
private: private:
// Port/pin activities set by set_pin_activity. // Port/pin activities set by set_pin_activity.

View File

@ -18,9 +18,9 @@
%{ %{
#include "Sta.hh" #include "Sta.hh"
#include "Sdc.hh"
#include "power/Power.hh" #include "power/Power.hh"
#include "power/VcdReader.hh" #include "power/VcdReader.hh"
#include "power/ReadVcdActivities.hh"
#include "power/SaifReader.hh" #include "power/SaifReader.hh"
using namespace sta; using namespace sta;
@ -102,6 +102,13 @@ set_power_pin_activity(const Pin *pin,
return power->setUserActivity(pin, activity, duty, PwrActivityOrigin::user); return power->setUserActivity(pin, activity, duty, PwrActivityOrigin::user);
} }
float
clock_min_period()
{
Power *power = Sta::sta()->power();
return power->clockMinPeriod();
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
void void
@ -113,22 +120,6 @@ read_vcd_file(const char *filename,
readVcdActivities(filename, scope, sta); readVcdActivities(filename, scope, sta);
} }
void
report_vcd_waveforms(const char *filename)
{
Sta *sta = Sta::sta();
reportVcdWaveforms(filename, sta);
}
// debugging
void
report_vcd_var_values(const char *filename,
const char *var_name)
{
Sta *sta = Sta::sta();
reportVcdVarValues(filename, var_name, sta);
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
bool bool
@ -140,4 +131,13 @@ read_saif_file(const char *filename,
return readSaif(filename, scope, sta); return readSaif(filename, scope, sta);
} }
void
report_activity_annotation_cmd(bool report_unannotated,
bool report_annotated)
{
Power *power = Sta::sta()->power();
power->reportActivityAnnotation(report_unannotated,
report_annotated);
}
%} // inline %} // inline

View File

@ -217,46 +217,67 @@ define_cmd_args "set_power_activity" { [-global]\
[-input]\ [-input]\
[-input_ports ports]\ [-input_ports ports]\
[-pins pins]\ [-pins pins]\
[-activity activity]\ [-activity activity | -density density]\
[-duty duty] } [-duty duty]\
[-clock clock]}
proc set_power_activity { args } { proc set_power_activity { args } {
parse_key_args "set_power_activity" args \ parse_key_args "set_power_activity" args \
keys {-input_ports -pins -activity -duty} \ keys {-input_ports -pins -activity -density -duty -clock} \
flags {-global -input} flags {-global -input}
check_argc_eq0 "set_power_activity" $args check_argc_eq0 "set_power_activity" $args
set activity 0.0 if { [info exists keys(-activity)] && [info exists keys(-density)] \
|| ![info exists keys(-activity)] && ![info exists keys(-density)] } {
sta_error 306 "Specify -activity or -density."
}
set density 0.0
if { [info exists keys(-activity)] } { if { [info exists keys(-activity)] } {
set activity $keys(-activity) set activity $keys(-activity)
check_float "activity" $activity check_positive_float "activity" $activity
if { $activity < 0.0 } { if { [info exists keys(-clock)] } {
sta_warn 301 "activity should be 0.0 to 1.0 or 2.0" set clk [get_clock_warn "-clock" $keys(-clock)]
} else {
set clks [get_clocks]
if { $clks == {} } {
sta_error 307 "-activity requires a clock to be defined"
}
}
set density [expr $activity / [clock_min_period]]
}
if { [info exists keys(-density)] } {
set density $keys(-density)
check_positive_float "density" $density
set density [expr $density / [time_ui_sta 1.0]]
if { [info exists keys(-clock)] } {
sta_warn 302 "-clock ignored for -density"
} }
} }
set duty 0.5 set duty 0.5
if { [info exists keys(-duty)] } { if { [info exists keys(-duty)] } {
set duty $keys(-duty) set duty $keys(-duty)
check_float "duty" $duty check_float "duty" $duty
if { $duty < 0.0 || $duty > 1.0 } { if { $duty < 0.0 || $duty > 1.0 } {i
sta_warn 302 "duty should be 0.0 to 1.0" sta_error 302 "duty should be 0.0 to 1.0"
} }
} }
if { [info exists flags(-global)] } { if { [info exists flags(-global)] } {
set_power_global_activity $activity $duty set_power_global_activity $density $duty
} }
if { [info exists flags(-input)] } { if { [info exists flags(-input)] } {
set_power_input_activity $activity $duty set_power_input_activity $density $duty
} }
if { [info exists keys(-input_ports)] } { if { [info exists keys(-input_ports)] } {
set ports [get_ports_error "input_ports" $keys(-input_ports)] set ports [get_ports_error "input_ports" $keys(-input_ports)]
foreach port $ports { foreach port $ports {
if { [get_property $port "direction"] == "input" } { if { [get_property $port "direction"] == "input" } {
if { [sta::is_clock_src [sta::get_port_pin $port]] } { if { [is_clock_src [sta::get_port_pin $port]] } {
sta_warn 303 "activity cannot be set on clock ports." sta_warn 303 "activity cannot be set on clock ports."
} else { } else {
set_power_input_port_activity $port $activity $duty set_power_input_port_activity $port $density $duty
} }
} }
} }
@ -264,7 +285,7 @@ proc set_power_activity { args } {
if { [info exists keys(-pins)] } { if { [info exists keys(-pins)] } {
set pins [get_pins $keys(-pins)] set pins [get_pins $keys(-pins)]
foreach pin $pins { foreach pin $pins {
set_power_pin_activity $pin $activity $duty set_power_pin_activity $pin $density $duty
} }
} }
} }
@ -323,6 +344,20 @@ proc read_saif { args } {
################################################################ ################################################################
define_cmd_args "report_activity_annotation" { [-report_unannotated] \
[-report_annotated] }
proc_redirect report_activity_annotation {
parse_key_args "report_activity_annotation" args \
keys {} flags {-report_unannotated -report_annotated}
check_argc_eq0 "report_activity_annotation" $args
set report_unannotated [info exists flags(-report_unannotated)]
set report_annotated [info exists flags(-report_annotated)];
report_activity_annotation_cmd $report_unannotated $report_annotated
}
################################################################
proc power_find_nan { } { proc power_find_nan { } {
set corner [cmd_corner] set corner [cmd_corner]
foreach inst [network_leaf_instances] { foreach inst [network_leaf_instances] {

View File

@ -1,258 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, 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/>.
#include "ReadVcdActivities.hh"
#include <inttypes.h>
#include <set>
#include "VcdReader.hh"
#include "Debug.hh"
#include "Network.hh"
#include "VerilogNamespace.hh"
#include "ParseBus.hh"
#include "Sdc.hh"
#include "Power.hh"
#include "Sta.hh"
namespace sta {
using std::abs;
using std::min;
using std::to_string;
class ReadVcdActivities : public StaState
{
public:
ReadVcdActivities(const char *filename,
const char *scope,
Sta *sta);
void readActivities();
private:
void setActivities();
void setVarActivity(VcdVar *var,
string &var_name,
const VcdValues &var_value);
void setVarActivity(const char *pin_name,
const VcdValues &var_values,
int value_bit);
void findVarActivity(const VcdValues &var_values,
int value_bit,
// Return values.
double &transition_count,
double &activity,
double &duty);
void checkClkPeriod(const Pin *pin,
double transition_count);
const char *filename_;
const char *scope_;
Vcd vcd_;
double clk_period_;
Sta *sta_;
Power *power_;
std::set<const Pin*> annotated_pins_;
static constexpr double sim_clk_period_tolerance_ = .1;
};
void
readVcdActivities(const char *filename,
const char *scope,
Sta *sta)
{
ReadVcdActivities reader(filename, scope, sta);
reader.readActivities();
}
ReadVcdActivities::ReadVcdActivities(const char *filename,
const char *scope,
Sta *sta) :
StaState(sta),
filename_(filename),
scope_(scope),
vcd_(sta),
clk_period_(0.0),
sta_(sta),
power_(sta->power())
{
}
void
ReadVcdActivities::readActivities()
{
vcd_ = readVcdFile(filename_, sta_);
clk_period_ = INF;
for (Clock *clk : *sta_->sdc()->clocks())
clk_period_ = min(static_cast<double>(clk->period()), clk_period_);
if (vcd_.timeMax() > 0)
setActivities();
else
report_->warn(1450, "VCD max time is zero.");
report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size());
}
void
ReadVcdActivities::setActivities()
{
size_t scope_length = strlen(scope_);
for (VcdVar *var : vcd_.vars()) {
const VcdValues &var_values = vcd_.values(var);
if (!var_values.empty()
&& (var->type() == VcdVarType::wire
|| var->type() == VcdVarType::reg)) {
string var_name = var->name();
// string::starts_with in c++20
if (scope_length) {
if (var_name.substr(0, scope_length) == scope_) {
var_name = var_name.substr(scope_length + 1);
setVarActivity(var, var_name, var_values);
}
}
else
setVarActivity(var, var_name, var_values);
}
}
}
void
ReadVcdActivities::setVarActivity(VcdVar *var,
string &var_name,
const VcdValues &var_values)
{
if (var->width() == 1) {
string sta_name = netVerilogToSta(var_name.c_str());
setVarActivity(sta_name.c_str(), var_values, 0);
}
else {
bool is_bus, is_range, subscript_wild;
string bus_name;
int from, to;
parseBusName(var_name.c_str(), '[', ']', '\\',
is_bus, is_range, bus_name, from, to, subscript_wild);
if (is_bus) {
string sta_bus_name = netVerilogToSta(bus_name.c_str());
int value_bit = 0;
if (to < from) {
for (int bus_bit = to; bus_bit <= from; bus_bit++) {
string pin_name = sta_bus_name;
pin_name += '[';
pin_name += to_string(bus_bit);
pin_name += ']';
setVarActivity(pin_name.c_str(), var_values, value_bit);
value_bit++;
}
}
else {
for (int bus_bit = to; bus_bit >= from; bus_bit--) {
string pin_name = sta_bus_name;
pin_name += '[';
pin_name += to_string(bus_bit);
pin_name += ']';
setVarActivity(pin_name.c_str(), var_values, value_bit);
value_bit++;
}
}
}
else
report_->warn(1451, "problem parsing bus %s.", var_name.c_str());
}
}
void
ReadVcdActivities::setVarActivity(const char *pin_name,
const VcdValues &var_values,
int value_bit)
{
const Pin *pin = sdc_network_->findPin(pin_name);
if (pin) {
debugPrint(debug_, "read_vcd_activities", 3, "%s values", pin_name);
double transition_count, activity, duty;
findVarActivity(var_values, value_bit,
transition_count, activity, duty);
debugPrint(debug_, "read_vcd_activities", 1,
"%s transitions %.1f activity %.2f duty %.2f",
pin_name,
transition_count,
activity,
duty);
if (sdc_->isLeafPinClock(pin))
checkClkPeriod(pin, transition_count);
power_->setUserActivity(pin, activity, duty, PwrActivityOrigin::vcd);
annotated_pins_.insert(pin);
}
}
void
ReadVcdActivities::findVarActivity(const VcdValues &var_values,
int value_bit,
// Return values.
double &transition_count,
double &activity,
double &duty)
{
transition_count = 0.0;
char prev_value = var_values[0].value(value_bit);
VcdTime prev_time = var_values[0].time();
VcdTime high_time = 0;
for (const VcdValue &var_value : var_values) {
VcdTime time = var_value.time();
char value = var_value.value(value_bit);
debugPrint(debug_, "read_vcd_activities", 3, " %" PRId64 " %c", time, value);
if (prev_value == '1')
high_time += time - prev_time;
if (value != prev_value)
transition_count += (value == 'X'
|| value == 'Z'
|| prev_value == 'X'
|| prev_value == 'Z')
? .5
: 1.0;
prev_time = time;
prev_value = value;
}
VcdTime time_max = vcd_.timeMax();
if (prev_value == '1')
high_time += time_max - prev_time;
duty = static_cast<double>(high_time) / time_max;
activity = transition_count / (time_max * vcd_.timeScale() / clk_period_);
}
void
ReadVcdActivities::checkClkPeriod(const Pin *pin,
double transition_count)
{
VcdTime time_max = vcd_.timeMax();
double sim_period = time_max * vcd_.timeScale() / (transition_count / 2.0);
ClockSet *clks = sdc_->findLeafPinClocks(pin);
if (clks) {
for (Clock *clk : *clks) {
double clk_period = clk->period();
if (abs((clk_period - sim_period) / clk_period) > .1)
// Warn if sim clock period differs from SDC by 10%.
report_->warn(1452, "clock %s vcd period %s differs from SDC clock period %s",
clk->name(),
delayAsString(sim_period, this),
delayAsString(clk_period, this));
}
}
}
}

View File

@ -1,28 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, 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/>.
#pragma once
namespace sta {
class Sta;
void
readVcdActivities(const char *filename,
const char *scope,
Sta *sta);
} // namespace

View File

@ -23,6 +23,7 @@
#include "Debug.hh" #include "Debug.hh"
#include "Report.hh" #include "Report.hh"
#include "Network.hh" #include "Network.hh"
#include "PortDirection.hh"
#include "Sdc.hh" #include "Sdc.hh"
#include "Power.hh" #include "Power.hh"
#include "power/SaifReaderPvt.hh" #include "power/SaifReaderPvt.hh"
@ -62,7 +63,6 @@ SaifReader::SaifReader(const char *filename,
escape_('\\'), escape_('\\'),
timescale_(1.0E-9F), // default units of ns timescale_(1.0E-9F), // default units of ns
duration_(0.0), duration_(0.0),
clk_period_(0.0),
in_scope_level_(0), in_scope_level_(0),
power_(sta->power()) power_(sta->power())
{ {
@ -78,10 +78,6 @@ SaifReader::read()
// Use zlib to uncompress gzip'd files automagically. // Use zlib to uncompress gzip'd files automagically.
stream_ = gzopen(filename_, "rb"); stream_ = gzopen(filename_, "rb");
if (stream_) { if (stream_) {
clk_period_ = INF;
for (Clock *clk : *sdc_->clocks())
clk_period_ = min(static_cast<double>(clk->period()), clk_period_);
saif_scope_.clear(); saif_scope_.clear();
in_scope_level_ = 0; in_scope_level_ = 0;
annotated_pins_.clear(); annotated_pins_.clear();
@ -178,20 +174,22 @@ SaifReader::setNetDurations(const char *net_name,
if (parent) { if (parent) {
string unescaped_name = unescaped(net_name); string unescaped_name = unescaped(net_name);
const Pin *pin = sdc_network_->findPin(parent, unescaped_name.c_str()); const Pin *pin = sdc_network_->findPin(parent, unescaped_name.c_str());
if (pin) { if (pin
&& !sdc_network_->isHierarchical(pin)
&& !sdc_network_->direction(pin)->isInternal()) {
double t1 = durations[static_cast<int>(SaifState::T1)]; double t1 = durations[static_cast<int>(SaifState::T1)];
float duty = t1 / duration_; float duty = t1 / duration_;
double tc = durations[static_cast<int>(SaifState::TC)]; double tc = durations[static_cast<int>(SaifState::TC)];
float activity = tc / (duration_ * timescale_ / clk_period_); float density = tc / (duration_ * timescale_);
debugPrint(debug_, "read_saif", 2, debugPrint(debug_, "read_saif", 2,
"%s duty %.0f / %" PRIu64 " = %.2f tc %.0f activity %.2f", "%s duty %.0f / %" PRIu64 " = %.2f tc %.0f density %.2f",
sdc_network_->pathName(pin), sdc_network_->pathName(pin),
t1, t1,
duration_, duration_,
duty, duty,
tc, tc,
activity); density);
power_->setUserActivity(pin, activity, duty, PwrActivityOrigin::saif); power_->setUserActivity(pin, density, duty, PwrActivityOrigin::saif);
annotated_pins_.insert(pin); annotated_pins_.insert(pin);
} }
} }

View File

@ -96,7 +96,6 @@ private:
char escape_; char escape_;
double timescale_; double timescale_;
int64_t duration_; int64_t duration_;
double clk_period_;
vector<string> saif_scope_; // Scope during parsing. vector<string> saif_scope_; // Scope during parsing.
size_t in_scope_level_; size_t in_scope_level_;

View File

@ -1,213 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, 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/>.
#include "Vcd.hh"
#include "Report.hh"
namespace sta {
Vcd::Vcd(StaState *sta) :
StaState(sta),
time_scale_(1.0),
time_unit_scale_(1.0),
max_var_name_length_(0),
max_var_width_(0),
min_delta_time_(0),
time_max_(0)
{
}
Vcd::Vcd(const Vcd &vcd) :
StaState(vcd),
date_(vcd.date_),
comment_(vcd.comment_),
version_(vcd.version_),
time_scale_(vcd.time_scale_),
time_unit_(vcd.time_unit_),
time_unit_scale_(vcd.time_unit_scale_),
vars_(vcd.vars_),
var_name_map_(vcd.var_name_map_),
max_var_name_length_(vcd.max_var_name_length_),
max_var_width_(vcd.max_var_width_),
id_values_map_(vcd.id_values_map_),
min_delta_time_(vcd.min_delta_time_),
time_max_(vcd.time_max_)
{
}
Vcd&
Vcd::operator=(Vcd &&vcd1)
{
date_ = vcd1.date_;
comment_ = vcd1.comment_;
version_ = vcd1.version_;
time_scale_ = vcd1.time_scale_;
time_unit_ = vcd1.time_unit_;
time_unit_scale_ = vcd1.time_unit_scale_;
vars_ = vcd1.vars_;
var_name_map_ = vcd1.var_name_map_;
max_var_name_length_ = vcd1.max_var_name_length_;
max_var_width_ = vcd1.max_var_width_;
id_values_map_ = vcd1.id_values_map_;
min_delta_time_ = vcd1.min_delta_time_;
time_max_ = vcd1.time_max_;
vcd1.vars_.clear();
return *this;
}
Vcd::~Vcd()
{
for (VcdVar *var : vars_)
delete var;
}
void
Vcd::setTimeUnit(const string &time_unit,
double time_unit_scale)
{
time_unit_ = time_unit;
time_unit_scale_ = time_unit_scale;
}
void
Vcd::setDate(const string &date)
{
date_ = date;
}
void
Vcd::setComment(const string &comment)
{
comment_ = comment;
}
void
Vcd::setVersion(const string &version)
{
version_ = version;
}
void
Vcd::setTimeScale(double time_scale)
{
time_scale_ = time_scale;
}
void
Vcd::setMinDeltaTime(VcdTime min_delta_time)
{
min_delta_time_ = min_delta_time;
}
void
Vcd::setTimeMax(VcdTime time_max)
{
time_max_ = time_max;
}
void
Vcd::makeVar(string &name,
VcdVarType type,
int width,
string &id)
{
VcdVar *var = new VcdVar(name, type, width, id);
vars_.push_back(var);
var_name_map_[name] = var;
max_var_name_length_ = std::max(max_var_name_length_, name.size());
max_var_width_ = std::max(max_var_width_, width);
// Make entry for var ID.
id_values_map_[id].clear();
}
VcdVar *
Vcd::var(const string name)
{
return var_name_map_[name];
}
bool
Vcd::varIdValid(string &id)
{
return id_values_map_.find(id) != id_values_map_.end();
}
void
Vcd::varAppendValue(string &id,
VcdTime time,
char value)
{
VcdValues &values = id_values_map_[id];
values.emplace_back(time, value, 0);
}
void
Vcd::varAppendBusValue(string &id,
VcdTime time,
int64_t bus_value)
{
VcdValues &values = id_values_map_[id];
values.emplace_back(time, '\0', bus_value);
}
VcdValues &
Vcd::values(VcdVar *var)
{
if (id_values_map_.find(var->id()) == id_values_map_.end()) {
report_->error(1360, "Unknown variable %s ID %s",
var->name().c_str(),
var->id().c_str());
static VcdValues empty;
return empty;
}
else
return id_values_map_[var->id()];
}
////////////////////////////////////////////////////////////////
VcdVar::VcdVar(string name,
VcdVarType type,
int width,
string id) :
name_(name),
type_(type),
width_(width),
id_(id)
{
}
VcdValue::VcdValue(VcdTime time,
char value,
uint64_t bus_value) :
time_(time),
value_(value),
bus_value_(bus_value)
{
}
char
VcdValue::value(int value_bit) const
{
if (value_ == '\0')
return ((bus_value_ >> value_bit) & 0x1) ? '1' : '0';
else
return value_;
}
}

View File

@ -1,158 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, 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/>.
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include "StaState.hh"
namespace sta {
using std::string;
using std::vector;
using std::map;
using std::max;
using std::min;
class VcdVar;
class VcdValue;
typedef vector<VcdValue> VcdValues;
typedef int64_t VcdTime;
typedef vector<string> VcdScope;
typedef map<string, VcdVar*> VcdNameMap;
enum class VcdVarType {
wire,
reg,
parameter,
integer,
real,
supply0,
supply1,
time,
tri,
triand,
trior,
trireg,
tri0,
tri1,
wand,
wor,
unknown
};
class Vcd : public StaState
{
public:
Vcd(StaState *sta);
Vcd(const Vcd &vcd);
// Move copy assignment.
Vcd& operator=(Vcd &&vcd1);
~Vcd();
VcdVar *var(const string name);
VcdValues &values(VcdVar *var);
const string &date() const { return date_; }
void setDate(const string &date);
const string &comment() const { return comment_; }
void setComment(const string &comment);
const string &version() const { return version_; }
void setVersion(const string &version);
double timeScale() const { return time_scale_; }
void setTimeScale(double time_scale);
const string &timeUnit() const { return time_unit_; }
double timeUnitScale() const { return time_unit_scale_; }
void setTimeUnit(const string &time_unit,
double time_unit_scale);
VcdTime timeMax() const { return time_max_; }
void setTimeMax(VcdTime time_max);
VcdTime minDeltaTime() const { return min_delta_time_; }
void setMinDeltaTime(VcdTime min_delta_time);
vector<VcdVar*> vars() { return vars_; }
void makeVar(string &name,
VcdVarType type,
int width,
string &id);
int maxVarWidth() const { return max_var_width_; }
int maxVarNameLength() const { return max_var_name_length_; }
bool varIdValid(string &id);
void varAppendValue(string &id,
VcdTime time,
char value);
void varAppendBusValue(string &id,
VcdTime time,
int64_t bus_value);
private:
string date_;
string comment_;
string version_;
double time_scale_;
string time_unit_;
double time_unit_scale_;
vector<VcdVar*> vars_;
VcdNameMap var_name_map_;
size_t max_var_name_length_;
int max_var_width_;
map<string, VcdValues> id_values_map_;
VcdTime min_delta_time_;
VcdTime time_max_;
};
class VcdVar
{
public:
VcdVar(string name,
VcdVarType type,
int width,
string id);
const string& name() const { return name_; }
VcdVarType type() const { return type_; }
int width() const { return width_; }
const string& id() const { return id_; }
private:
string name_;
VcdVarType type_;
int width_;
string id_;
};
class VcdValue
{
public:
VcdValue(VcdTime time,
char value,
uint64_t bus_value);
VcdTime time() const { return time_; }
char value() const { return value_; }
uint64_t busValue() const { return bus_value_; }
char value(int value_bit) const;
private:
VcdTime time_;
// 01XUZ or '\0' when width > 1 to use bus_value_.
char value_;
uint64_t bus_value_;
};
} // namespace

318
power/VcdParse.cc Normal file
View File

@ -0,0 +1,318 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, 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/>.
#include "VcdParse.hh"
#include <cctype>
#include <cinttypes>
#include "Stats.hh"
#include "Report.hh"
#include "Error.hh"
#include "EnumNameMap.hh"
namespace sta {
using std::isspace;
// Very imprecise syntax definition
// https://en.wikipedia.org/wiki/Value_change_dump#Structure.2FSyntax
// Much better syntax definition
// https://web.archive.org/web/20120323132708/http://www.beyondttl.com/vcd.php
void
VcdParse::read(const char *filename,
VcdReader *reader)
{
stream_ = gzopen(filename, "r");
if (stream_) {
Stats stats(debug_, report_);
filename_ = filename;
reader_ = reader;
file_line_ = 1;
stmt_line_ = 1;
string token = getToken();
while (!token.empty()) {
if (token == "$date")
reader_->setDate(readStmtString());
else if (token == "$comment")
reader_->setComment(readStmtString());
else if (token == "$version")
reader_->setVersion(readStmtString());
else if (token == "$timescale")
parseTimescale();
else if (token == "$var")
parseVar();
else if (token == "$scope")
parseScope();
else if (token == "$upscope")
parseUpscope();
else if (token == "$enddefinitions")
// empty body
readStmtString();
else if (token == "$dumpall")
parseVarValues();
else if (token == "$dumpvars")
// Initial values.
parseVarValues();
else if (token[0] == '$')
report_->fileError(800, filename_, stmt_line_, "unhandled vcd command.");
else
parseVarValues();
token = getToken();
}
gzclose(stream_);
stats.report("Read VCD");
}
else
throw FileNotReadable(filename);
}
VcdParse::VcdParse(Report *report,
Debug *debug) :
reader_(nullptr),
file_line_(0),
stmt_line_(0),
time_(0),
prev_time_(0),
report_(report),
debug_(debug)
{
}
void
VcdParse::parseTimescale()
{
vector<string> tokens = readStmtTokens();
if (tokens.size() == 1) {
size_t last;
double time_scale = std::stod(tokens[0], &last);
setTimeUnit(tokens[0].substr(last), time_scale);
}
else if (tokens.size() == 2) {
double time_scale = std::stod(tokens[0]);
setTimeUnit(tokens[1], time_scale);
}
else
report_->fileError(801, filename_, stmt_line_, "timescale syntax error.");
}
void
VcdParse::setTimeUnit(const string &time_unit,
double time_scale)
{
double time_unit_scale = 1.0;
if (time_unit == "fs")
time_unit_scale = 1e-15;
else if (time_unit == "ps")
time_unit_scale = 1e-12;
else if (time_unit == "ns")
time_unit_scale = 1e-9;
else
report_->fileError(802, filename_, stmt_line_, "Unknown timescale unit.");
reader_->setTimeUnit(time_unit, time_unit_scale, time_scale);
}
static EnumNameMap<VcdVarType> vcd_var_type_map =
{{VcdVarType::wire, "wire"},
{VcdVarType::reg, "reg"},
{VcdVarType::parameter, "parameter"},
{VcdVarType::integer, "integer"},
{VcdVarType::real, "real"},
{VcdVarType::supply0, "supply0"},
{VcdVarType::supply1, "supply1"},
{VcdVarType::time, "time"},
{VcdVarType::tri, "tri"},
{VcdVarType::triand, "triand"},
{VcdVarType::trior, "trior"},
{VcdVarType::trireg, "trireg"},
{VcdVarType::tri0, "tri0"},
{VcdVarType::tri1, "tri1"},
{VcdVarType::wand, "wand"},
{VcdVarType::wor, "wor"}
};
void
VcdParse::parseVar()
{
vector<string> tokens = readStmtTokens();
if (tokens.size() == 4
|| tokens.size() == 5) {
string type_name = tokens[0];
VcdVarType type = vcd_var_type_map.find(type_name, VcdVarType::unknown);
if (type == VcdVarType::unknown)
report_->fileWarn(1370, filename_, stmt_line_,
"Unknown variable type %s.",
type_name.c_str());
else {
size_t width = stoi(tokens[1]);
string &id = tokens[2];
string name = tokens[3];
// iverilog separates bus base name from bit range.
if (tokens.size() == 5) {
// Preserve space after esacaped name.
if (name[0] == '\\')
name += ' ';
name += tokens[4];
}
reader_->makeVar(scope_, name, type, width, id);
}
}
else
report_->fileError(804, filename_, stmt_line_, "Variable syntax error.");
}
void
VcdParse::parseScope()
{
vector<string> tokens = readStmtTokens();
string &scope = tokens[1];
scope_.push_back(scope);
}
void
VcdParse::parseUpscope()
{
readStmtTokens();
scope_.pop_back();
}
void
VcdParse::parseVarValues()
{
string token = getToken();
while (!token.empty()) {
char char0 = toupper(token[0]);
if (char0 == '#' && token.size() > 1) {
prev_time_ = time_;
time_ = stoll(token.substr(1));
if (time_ > prev_time_)
reader_->varMinDeltaTime(time_ - prev_time_);
}
else if (char0 == '0'
|| char0 == '1'
|| char0 == 'X'
|| char0 == 'U'
|| char0 == 'Z') {
string id = token.substr(1);
if (!reader_->varIdValid(id))
report_->fileError(805, filename_, stmt_line_,
"unknown variable %s", id.c_str());
reader_->varAppendValue(id, time_, char0);
}
else if (char0 == 'B') {
char char1 = toupper(token[1]);
if (char1 == 'X'
|| char1 == 'U'
|| char1 == 'Z') {
string id = getToken();
if (!reader_->varIdValid(id))
report_->fileError(806, filename_, stmt_line_,
"unknown variable %s", id.c_str());
// Bus mixed 0/1/X/U not supported.
reader_->varAppendValue(id, time_, char1);
}
else {
string bin = token.substr(1);
char *end;
int64_t bus_value = strtol(bin.c_str(), &end, 2);
string id = getToken();
if (!reader_->varIdValid(id))
report_->fileError(807, filename_, stmt_line_,
"unknown variable %s", id.c_str());
else
reader_->varAppendBusValue(id, time_, bus_value);
}
}
token = getToken();
}
reader_->setTimeMax(time_);
}
string
VcdParse::readStmtString()
{
stmt_line_ = file_line_;
string line;
string token = getToken();
while (!token.empty() && token != "$end") {
if (!line.empty())
line += " ";
line += token;
token = getToken();
}
return line;
}
vector<string>
VcdParse::readStmtTokens()
{
stmt_line_ = file_line_;
vector<string> tokens;
string token = getToken();
while (!token.empty() && token != "$end") {
tokens.push_back(token);
token = getToken();
}
return tokens;
}
string
VcdParse::getToken()
{
string token;
int ch = gzgetc(stream_);
if (ch == '\n')
file_line_++;
// skip whitespace
while (ch != EOF && isspace(ch)) {
ch = gzgetc(stream_);
if (ch == '\n')
file_line_++;
}
while (ch != EOF && !isspace(ch)) {
token.push_back(ch);
ch = gzgetc(stream_);
if (ch == '\n')
file_line_++;
}
if (ch == EOF)
return "";
else
return token;
}
////////////////////////////////////////////////////////////////
char
VcdValue::value(int value_bit) const
{
if (value_ == '\0')
return ((bus_value_ >> value_bit) & 0x1) ? '1' : '0';
else
return value_;
}
void
VcdValue::setValue(VcdTime time,
char value)
{
time_ = time;
value_ = value;
}
} // namespace

136
power/VcdParse.hh Normal file
View File

@ -0,0 +1,136 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, 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/>.
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include "Zlib.hh"
#include "StaState.hh"
namespace sta {
using std::string;
using std::vector;
typedef int64_t VcdTime;
typedef vector<string> VcdScope;
enum class VcdVarType {
wire,
reg,
parameter,
integer,
real,
supply0,
supply1,
time,
tri,
triand,
trior,
trireg,
tri0,
tri1,
wand,
wor,
unknown
};
class VcdReader;
class VcdParse : public StaState
{
public:
VcdParse(Report *report,
Debug *debug);
void read(const char *filename,
VcdReader *reader);
private:
void parseTimescale();
void setTimeUnit(const string &time_unit,
double time_scale);
void parseVar();
void parseScope();
void parseUpscope();
void parseVarValues();
string getToken();
string readStmtString();
vector<string> readStmtTokens();
VcdReader *reader_;
gzFile stream_;
string token_;
const char *filename_;
int file_line_;
int stmt_line_;
VcdTime time_;
VcdTime prev_time_;
VcdScope scope_;
Report *report_;
Debug *debug_;
};
// Abstract class for VcdParse callbacks.
class VcdReader
{
public:
virtual ~VcdReader() {}
virtual void setDate(const string &date) = 0;
virtual void setComment(const string &comment) = 0;
virtual void setVersion(const string &version) = 0;
virtual void setTimeUnit(const string &time_unit,
double time_unit_scale,
double time_scale) = 0;
virtual void setTimeMax(VcdTime time_max) = 0;
virtual void varMinDeltaTime(VcdTime min_delta_time) = 0;
virtual bool varIdValid(const string &id) = 0;
virtual void makeVar(const VcdScope &scope,
const string &name,
VcdVarType type,
size_t width,
const string &id) = 0;
virtual void varAppendValue(const string &id,
VcdTime time,
char value) = 0;
virtual void varAppendBusValue(const string &id,
VcdTime time,
int64_t bus_value) = 0;
};
class VcdValue
{
public:
VcdValue();
VcdTime time() const { return time_; }
char value() const { return value_; }
void setValue(VcdTime time,
char value);
uint64_t busValue() const { return bus_value_; }
char value(int value_bit) const;
private:
VcdTime time_;
// 01XUZ or '\0' when width > 1 to use bus_value_.
char value_;
uint64_t bus_value_;
};
} // namespace

View File

@ -14,437 +14,428 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cctype>
#include <cinttypes>
#include "VcdReader.hh" #include "VcdReader.hh"
#include "Zlib.hh" #include <inttypes.h>
#include "Stats.hh" #include <set>
#include "Report.hh"
#include "Error.hh" #include "VcdParse.hh"
#include "StringUtil.hh" #include "Debug.hh"
#include "EnumNameMap.hh" #include "Network.hh"
#include "PortDirection.hh"
#include "VerilogNamespace.hh"
#include "ParseBus.hh"
#include "Sdc.hh"
#include "Power.hh"
#include "Sta.hh"
namespace sta { namespace sta {
using std::isspace; using std::abs;
using std::min;
using std::to_string;
using std::vector;
using std::map;
// Very imprecise syntax definition // Transition count and high time for duty cycle for a group of pins
// https://en.wikipedia.org/wiki/Value_change_dump#Structure.2FSyntax // for one bit of vcd ID.
// Much better syntax definition class VcdCount
// https://web.archive.org/web/20120323132708/http://www.beyondttl.com/vcd.php
class VcdReader : public StaState
{ {
public: public:
VcdReader(StaState *sta); VcdCount();
Vcd read(const char *filename); double transitionCount() const { return transition_count_; }
VcdTime highTime(VcdTime time_max) const;
void incrCounts(VcdTime time,
char value);
void incrCounts(VcdTime time,
int64_t value);
void addPin(const Pin *pin);
const PinSeq &pins() const { return pins_; }
private: private:
void parseTimescale(); PinSeq pins_;
void setTimeUnit(const string &time_unit);
void parseVar();
void parseScope();
void parseUpscope();
void parseVarValues();
string getToken();
string readStmtString();
vector<string> readStmtTokens();
gzFile stream_;
string token_;
const char *filename_;
int file_line_;
int stmt_line_;
Vcd *vcd_;
VcdTime time_;
VcdTime prev_time_; VcdTime prev_time_;
VcdScope scope_; char prev_value_;
VcdTime high_time_;
double transition_count_;
}; };
Vcd VcdCount::VcdCount() :
readVcdFile(const char *filename, prev_time_(-1),
StaState *sta) prev_value_('\0'),
high_time_(0),
transition_count_(0)
{ {
VcdReader reader(sta);
return reader.read(filename);
} }
Vcd void
VcdReader::read(const char *filename) VcdCount::addPin(const Pin *pin)
{ {
Vcd vcd(this); pins_.push_back(pin);
vcd_ = &vcd; }
stream_ = gzopen(filename, "r");
if (stream_) { void
Stats stats(debug_, report_); VcdCount::incrCounts(VcdTime time,
filename_ = filename; char value)
file_line_ = 1; {
stmt_line_ = 1; // Initial value does not coontribute to transitions or high time.
string token = getToken(); if (prev_time_ != -1) {
while (!token.empty()) { if (prev_value_ == '1')
if (token == "$date") high_time_ += time - prev_time_;
vcd_->setDate(readStmtString()); if (value != prev_value_)
else if (token == "$comment") transition_count_ += (value == 'X'
vcd_->setComment(readStmtString()); || value == 'Z'
else if (token == "$version") || prev_value_ == 'X'
vcd_->setVersion(readStmtString()); || prev_value_ == 'Z')
else if (token == "$timescale") ? .5
parseTimescale(); : 1.0;
else if (token == "$var")
parseVar();
else if (token == "$scope")
parseScope();
else if (token == "$upscope")
parseUpscope();
else if (token == "$enddefinitions")
// empty body
readStmtString();
else if (token == "$dumpall")
parseVarValues();
else if (token == "$dumpvars")
// Initial values.
parseVarValues();
else if (token[0] == '$')
report_->fileError(800, filename_, stmt_line_, "unhandled vcd command.");
else
parseVarValues();
token = getToken();
}
gzclose(stream_);
stats.report("Read VCD");
} }
prev_time_ = time;
prev_value_ = value;
}
VcdTime
VcdCount::highTime(VcdTime time_max) const
{
if (prev_value_ == '1')
return high_time_ + time_max - prev_time_;
else else
throw FileNotReadable(filename); return high_time_;
return vcd;
}
VcdReader::VcdReader(StaState *sta) :
StaState(sta),
file_line_(0),
stmt_line_(0),
vcd_(nullptr),
time_(0),
prev_time_(0)
{
}
void
VcdReader::parseTimescale()
{
vector<string> tokens = readStmtTokens();
if (tokens.size() == 1) {
size_t last;
double time_scale = std::stod(tokens[0], &last);
setTimeUnit(tokens[0].substr(last));
vcd_->setTimeScale(time_scale * vcd_->timeUnitScale());
}
else if (tokens.size() == 2) {
setTimeUnit(tokens[1]);
double time_scale = std::stod(tokens[0]);
vcd_->setTimeScale(time_scale * vcd_->timeUnitScale());
}
else
report_->fileError(801, filename_, stmt_line_, "timescale syntax error.");
}
void
VcdReader::setTimeUnit(const string &time_unit)
{
double time_unit_scale = 1.0;
if (time_unit == "fs")
time_unit_scale = 1e-15;
else if (time_unit == "ps")
time_unit_scale = 1e-12;
else if (time_unit == "ns")
time_unit_scale = 1e-9;
else
report_->fileError(802, filename_, stmt_line_, "Unknown timescale unit.");
vcd_->setTimeUnit(time_unit, time_unit_scale);;
}
static EnumNameMap<VcdVarType> vcd_var_type_map =
{{VcdVarType::wire, "wire"},
{VcdVarType::reg, "reg"},
{VcdVarType::parameter, "parameter"},
{VcdVarType::integer, "integer"},
{VcdVarType::real, "real"},
{VcdVarType::supply0, "supply0"},
{VcdVarType::supply1, "supply1"},
{VcdVarType::time, "time"},
{VcdVarType::tri, "tri"},
{VcdVarType::triand, "triand"},
{VcdVarType::trior, "trior"},
{VcdVarType::trireg, "trireg"},
{VcdVarType::tri0, "tri0"},
{VcdVarType::tri1, "tri1"},
{VcdVarType::wand, "wand"},
{VcdVarType::wor, "wor"}
};
void
VcdReader::parseVar()
{
vector<string> tokens = readStmtTokens();
if (tokens.size() == 4
|| tokens.size() == 5) {
string type_name = tokens[0];
VcdVarType type = vcd_var_type_map.find(type_name, VcdVarType::unknown);
if (type == VcdVarType::unknown)
report_->fileWarn(1370, filename_, stmt_line_,
"Unknown variable type %s.",
type_name.c_str());
else {
int width = stoi(tokens[1]);
string &id = tokens[2];
string name;
for (string &context : scope_) {
name += context;
name += '/';
}
name += tokens[3];
// iverilog separates bus base name from bit range.
if (tokens.size() == 5) {
// Preserve space after esacaped name.
if (name[0] == '\\')
name += ' ';
name += tokens[4];
}
vcd_->makeVar(name, type, width, id);
}
}
else
report_->fileError(804, filename_, stmt_line_, "Variable syntax error.");
}
void
VcdReader::parseScope()
{
vector<string> tokens = readStmtTokens();
string &scope = tokens[1];
scope_.push_back(scope);
}
void
VcdReader::parseUpscope()
{
readStmtTokens();
scope_.pop_back();
}
void
VcdReader::parseVarValues()
{
string token = getToken();
while (!token.empty()) {
char char0 = toupper(token[0]);
if (char0 == '#' && token.size() > 1) {
prev_time_ = time_;
time_ = stoll(token.substr(1));
if (time_ > prev_time_)
vcd_->setMinDeltaTime(min(time_ - prev_time_, vcd_->minDeltaTime()));
}
else if (char0 == '0'
|| char0 == '1'
|| char0 == 'X'
|| char0 == 'U'
|| char0 == 'Z') {
string id = token.substr(1);
if (!vcd_->varIdValid(id))
report_->fileError(805, filename_, stmt_line_,
"unknown variable %s", id.c_str());
vcd_->varAppendValue(id, time_, char0);
}
else if (char0 == 'B') {
char char1 = toupper(token[1]);
if (char1 == 'X'
|| char1 == 'U'
|| char1 == 'Z') {
string id = getToken();
if (!vcd_->varIdValid(id))
report_->fileError(806, filename_, stmt_line_,
"unknown variable %s", id.c_str());
// Bus mixed 0/1/X/U not supported.
vcd_->varAppendValue(id, time_, char1);
}
else {
string bin = token.substr(1);
char *end;
int64_t bus_value = strtol(bin.c_str(), &end, 2);
string id = getToken();
if (!vcd_->varIdValid(id))
report_->fileError(807, filename_, stmt_line_,
"unknown variable %s", id.c_str());
else
vcd_->varAppendBusValue(id, time_, bus_value);
}
}
token = getToken();
}
vcd_->setTimeMax(time_);
}
string
VcdReader::readStmtString()
{
stmt_line_ = file_line_;
string line;
string token = getToken();
while (!token.empty() && token != "$end") {
if (!line.empty())
line += " ";
line += token;
token = getToken();
}
return line;
}
vector<string>
VcdReader::readStmtTokens()
{
stmt_line_ = file_line_;
vector<string> tokens;
string token = getToken();
while (!token.empty() && token != "$end") {
tokens.push_back(token);
token = getToken();
}
return tokens;
}
string
VcdReader::getToken()
{
string token;
int ch = gzgetc(stream_);
if (ch == '\n')
file_line_++;
// skip whitespace
while (ch != EOF && isspace(ch)) {
ch = gzgetc(stream_);
if (ch == '\n')
file_line_++;
}
while (ch != EOF && !isspace(ch)) {
token.push_back(ch);
ch = gzgetc(stream_);
if (ch == '\n')
file_line_++;
}
if (ch == EOF)
return "";
else
return token;
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
static void // VcdCount[bit]
reportWaveforms(Vcd &vcd, typedef vector<VcdCount> VcdCounts;
Report *report); // ID -> VcdCount[bit]
typedef map<string, VcdCounts> VcdIdCountsMap;
void
reportVcdWaveforms(const char *filename,
StaState *sta)
class VcdCountReader : public VcdReader
{
public:
VcdCountReader(const char *scope,
Network *sdc_network,
Report *report,
Debug *debug);
VcdTime timeMax() const { return time_max_; }
const VcdIdCountsMap &countMap() const { return vcd_count_map_; }
double timeScale() const { return time_scale_; }
// VcdParse callbacks.
void setDate(const string &) override {}
void setComment(const string &) override {}
void setVersion(const string &) override {}
void setTimeUnit(const string &time_unit,
double time_unit_scale,
double time_scale) override;
void setTimeMax(VcdTime time_max) override;
void varMinDeltaTime(VcdTime) override {}
bool varIdValid(const string &id) override;
void makeVar(const VcdScope &scope,
const string &name,
VcdVarType type,
size_t width,
const string &id) override;
void varAppendValue(const string &id,
VcdTime time,
char value) override;
void varAppendBusValue(const string &id,
VcdTime time,
int64_t bus_value) override;
private:
void addVarPin(const string &pin_name,
const string &id,
size_t width,
size_t bit_idx);
const char *scope_;
Network *sdc_network_;
Report *report_;
Debug *debug_;
double time_scale_;
VcdTime time_max_;
VcdIdCountsMap vcd_count_map_;
};
VcdCountReader::VcdCountReader(const char *scope,
Network *sdc_network,
Report *report,
Debug *debug) :
scope_(scope),
sdc_network_(sdc_network),
report_(report),
debug_(debug),
time_scale_(1.0),
time_max_(0.0)
{ {
Vcd vcd = readVcdFile(filename, sta);
reportWaveforms(vcd, sta->report());
} }
static void void
reportWaveforms(Vcd &vcd, VcdCountReader::setTimeUnit(const string &,
Report *report) double time_unit_scale,
double time_scale)
{ {
report->reportLine("Date: %s", vcd.date().c_str()); time_scale_ = time_scale * time_unit_scale;
report->reportLine("Timescale: %.2f%s", vcd.timeScale(), vcd.timeUnit().c_str()); }
// Characters per time sample.
int zoom = (vcd.maxVarWidth() + 7) / 4;
int time_delta = vcd.minDeltaTime();
int max_var_name_length = vcd.maxVarNameLength(); void
for (VcdVar *var : vcd.vars()) { VcdCountReader::setTimeMax(VcdTime time_max)
string line; {
stringPrint(line, " %-*s", time_max_ = time_max;
static_cast<int>(max_var_name_length), }
var->name().c_str());
const VcdValues &var_values = vcd.values(var); bool
if (!var_values.empty()) { VcdCountReader::varIdValid(const string &)
size_t value_index = 0; {
VcdValue var_value = var_values[value_index]; return true;
VcdValue prev_var_value = var_values[value_index]; }
VcdTime next_value_time = var_values[value_index + 1].time();
for (double time = 0.0; time < vcd.timeMax(); time += time_delta) { void
if (time >= next_value_time) { VcdCountReader::makeVar(const VcdScope &scope,
if (value_index < var_values.size() - 1) const string &name,
value_index++; VcdVarType type,
var_value = var_values[value_index]; size_t width,
if (value_index < var_values.size()) const string &id)
next_value_time = var_values[value_index + 1].time(); {
} if (type == VcdVarType::wire
if (var_value.value()) { || type == VcdVarType::reg) {
// 01UZX string path_name;
char value = var_value.value(); bool first = true;
char prev_value = prev_var_value.value(); for (const string &context : scope) {
if (var->width() == 1) { if (!first)
if (value == '0' || value == '1') { path_name += '/';
for (int z = 0; z < zoom; z++) { path_name += context;
if (z == 0 first = false;
&& value != prev_value }
&& (prev_value == '0' size_t scope_length = strlen(scope_);
|| prev_value == '1')) // string::starts_with in c++20
line += (prev_value == '1') ? "" : ""; if (scope_length == 0
else || path_name.substr(0, scope_length) == scope_) {
line += (value == '1') ? "" : ""; path_name += '/';
} path_name += name;
} // Strip the scope from the name.
else { string var_scoped = path_name.substr(scope_length + 1);
string field; if (width == 1) {
stringPrint(field, "%-*c", zoom, value); string pin_name = netVerilogToSta(var_scoped.c_str());
line += field; addVarPin(pin_name, id, width, 0);
}
else {
bool is_bus, is_range, subscript_wild;
string bus_name;
int from, to;
parseBusName(var_scoped.c_str(), '[', ']', '\\',
is_bus, is_range, bus_name, from, to, subscript_wild);
if (is_bus) {
string sta_bus_name = netVerilogToSta(bus_name.c_str());
int bit_idx = 0;
if (to < from) {
for (int bus_bit = to; bus_bit <= from; bus_bit++) {
string pin_name = sta_bus_name;
pin_name += '[';
pin_name += to_string(bus_bit);
pin_name += ']';
addVarPin(pin_name, id, width, bit_idx);
bit_idx++;
} }
} }
else { else {
string field; for (int bus_bit = to; bus_bit >= from; bus_bit--) {
stringPrint(field, "%-*c", zoom, value); string pin_name = sta_bus_name;
line += field; pin_name += '[';
pin_name += to_string(bus_bit);
pin_name += ']';
addVarPin(pin_name, id, width, bit_idx);
bit_idx++;
}
} }
} }
else { else
// bus report_->warn(1451, "problem parsing bus %s.", var_scoped.c_str());
string field;
stringPrint(field, "%-*" PRIX64, zoom, var_value.busValue());
line += field;
}
prev_var_value = var_value;
} }
} }
report->reportLineString(line);
} }
} }
void void
reportVcdVarValues(const char *filename, VcdCountReader::addVarPin(const string &pin_name,
const char *var_name, const string &id,
StaState *sta) size_t width,
size_t bit_idx)
{ {
Vcd vcd = readVcdFile(filename, sta); const Pin *pin = sdc_network_->findPin(pin_name.c_str());
VcdVar *var = vcd.var(var_name); if (pin
if (var) { && !sdc_network_->isHierarchical(pin)
Report *report = sta->report(); && !sdc_network_->direction(pin)->isInternal()) {
for (const VcdValue &var_value : vcd.values(var)) { VcdCounts &vcd_counts = vcd_count_map_[id];
double time = var_value.time() * vcd.timeUnitScale(); vcd_counts.resize(width);
char value = var_value.value(); vcd_counts[bit_idx].addPin(pin);
if (value == '\0') debugPrint(debug_, "read_vcd_activities", 2, "id %s pin %s",
report->reportLine("%.2e %" PRIu64, id.c_str(),
time, var_value.busValue()); pin_name.c_str());
else }
report->reportLine("%.2e %c", time, value); }
void
VcdCountReader::varAppendValue(const string &id,
VcdTime time,
char value)
{
auto itr = vcd_count_map_.find(id);
if (itr != vcd_count_map_.end()) {
VcdCounts &vcd_counts = itr->second;
if (debug_->check("read_vcd_activities", 3)) {
for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) {
VcdCount &vcd_count = vcd_counts[bit_idx];
for (const Pin *pin : vcd_count.pins()) {
debugPrint(debug_, "read_vcd_activities", 3, "%s time %" PRIu64 " value %c",
sdc_network_->pathName(pin),
time,
value);
}
}
}
for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) {
VcdCount &vcd_count = vcd_counts[bit_idx];
vcd_count.incrCounts(time, value);
}
}
}
void
VcdCountReader::varAppendBusValue(const string &id,
VcdTime time,
int64_t bus_value)
{
auto itr = vcd_count_map_.find(id);
if (itr != vcd_count_map_.end()) {
VcdCounts &vcd_counts = itr->second;
for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) {
char bit_value = ((bus_value >> bit_idx) & 0x1) ? '1' : '0';
VcdCount &vcd_count = vcd_counts[bit_idx];
if (debug_->check("read_vcd_activities", 3)) {
for (const Pin *pin : vcd_count.pins()) {
debugPrint(debug_, "read_vcd_activities", 3, "%s time %" PRIu64 " value %c",
sdc_network_->pathName(pin),
time,
bit_value);
}
}
vcd_count.incrCounts(time, bit_value);
}
}
}
////////////////////////////////////////////////////////////////
class ReadVcdActivities : public StaState
{
public:
ReadVcdActivities(const char *filename,
const char *scope,
Sta *sta);
void readActivities();
private:
void setActivities();
void checkClkPeriod(const Pin *pin,
double transition_count);
const char *filename_;
VcdCountReader vcd_reader_;
VcdParse vcd_parse_;
Power *power_;
std::set<const Pin*> annotated_pins_;
static constexpr double sim_clk_period_tolerance_ = .1;
};
void
readVcdActivities(const char *filename,
const char *scope,
Sta *sta)
{
ReadVcdActivities reader(filename, scope, sta);
reader.readActivities();
}
ReadVcdActivities::ReadVcdActivities(const char *filename,
const char *scope,
Sta *sta) :
StaState(sta),
filename_(filename),
vcd_reader_(scope, sdc_network_, report_, debug_),
vcd_parse_(report_, debug_),
power_(sta->power())
{
}
void
ReadVcdActivities::readActivities()
{
ClockSeq *clks = sdc_->clocks();
if (clks->empty())
report_->error(805, "No clocks have been defined.");
vcd_parse_.read(filename_, &vcd_reader_);
if (vcd_reader_.timeMax() > 0)
setActivities();
else
report_->warn(1450, "VCD max time is zero.");
report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size());
}
void
ReadVcdActivities::setActivities()
{
VcdTime time_max = vcd_reader_.timeMax();
double time_scale = vcd_reader_.timeScale();
for (auto& [id, vcd_counts] : vcd_reader_.countMap()) {
for (const VcdCount &vcd_count : vcd_counts) {
double transition_count = vcd_count.transitionCount();
VcdTime high_time = vcd_count.highTime(time_max);
float duty = static_cast<double>(high_time) / time_max;
float density = transition_count / (time_max * time_scale);
if (debug_->check("read_vcd_activities", 1)) {
for (const Pin *pin : vcd_count.pins()) {
debugPrint(debug_, "read_vcd_activities", 1,
"%s transitions %.1f activity %.2f duty %.2f",
sdc_network_->pathName(pin),
transition_count,
density,
duty);
}
}
for (const Pin *pin : vcd_count.pins()) {
power_->setUserActivity(pin, density, duty, PwrActivityOrigin::vcd);
if (sdc_->isLeafPinClock(pin))
checkClkPeriod(pin, transition_count);
annotated_pins_.insert(pin);
}
}
}
}
void
ReadVcdActivities::checkClkPeriod(const Pin *pin,
double transition_count)
{
VcdTime time_max = vcd_reader_.timeMax();
double time_scale = vcd_reader_.timeScale();
double sim_period = time_max * time_scale / (transition_count / 2.0);
ClockSet *clks = sdc_->findLeafPinClocks(pin);
if (clks) {
for (Clock *clk : *clks) {
double clk_period = clk->period();
if (abs((clk_period - sim_period) / clk_period) > sim_clk_period_tolerance_)
// Warn if sim clock period differs from SDC by more than 10%.
report_->warn(1452, "clock %s vcd period %s differs from SDC clock period %s",
clk->name(),
delayAsString(sim_period, this),
delayAsString(clk_period, this));
} }
} }
} }

View File

@ -16,24 +16,13 @@
#pragma once #pragma once
#include "Vcd.hh"
namespace sta { namespace sta {
class StaState; class Sta;
Vcd
readVcdFile(const char *filename,
StaState *sta);
void void
reportVcdWaveforms(const char *filename, readVcdActivities(const char *filename,
StaState *sta); const char *scope,
Sta *sta);
void
reportVcdVarValues(const char *filename,
const char *var_name,
StaState *sta);
} // namespace } // namespace

View File

@ -73,6 +73,9 @@ proc source_ { filename echo verbose } {
sta_error 340 "cannot open '$filename'." sta_error 340 "cannot open '$filename'."
} else { } else {
if { [file extension $filename] == ".gz" } { if { [file extension $filename] == ".gz" } {
if { [info commands zlib] == "" } {
sta_error 339 "tcl version > 8.6 required for zlib support."
}
zlib push gunzip $stream zlib push gunzip $stream
} }
# Save file and line in recursive call to source. # Save file and line in recursive call to source.

View File

@ -56,7 +56,7 @@ proc_redirect read_sdf {
################################################################ ################################################################
define_cmd_args "report_annotated_delay" \ define_cmd_args "report_annotated_delay" \
{[-cell] [-net] [-from_in_ports] [-to_out_ports] [-max_lines liness]\ {[-cell] [-net] [-from_in_ports] [-to_out_ports] [-max_lines lines]\
[-list_annotated] [-list_not_annotated] [-constant_arcs]} [-list_annotated] [-list_not_annotated] [-constant_arcs]}
proc_redirect report_annotated_delay { proc_redirect report_annotated_delay {
@ -92,7 +92,7 @@ proc_redirect report_annotated_delay {
define_cmd_args "report_annotated_check" \ define_cmd_args "report_annotated_check" \
{[-setup] [-hold] [-recovery] [-removal] [-nochange] [-width] [-period]\ {[-setup] [-hold] [-recovery] [-removal] [-nochange] [-width] [-period]\
[-max_skew] [-max_lines liness] [-list_annotated] [-list_not_annotated]\ [-max_skew] [-max_lines lines] [-list_annotated] [-list_not_annotated]\
[-constant_arcs]} [-constant_arcs]}
proc_redirect report_annotated_check { proc_redirect report_annotated_check {

View File

@ -51,8 +51,7 @@ PathEnd::~PathEnd()
} }
void void
PathEnd::setPath(PathEnumed *path, PathEnd::setPath(const Path *path)
const StaState *)
{ {
path_.init(path); path_.init(path);
} }
@ -523,8 +522,7 @@ PathEndClkConstrained::PathEndClkConstrained(Path *path,
} }
void void
PathEndClkConstrained::setPath(PathEnumed *path, PathEndClkConstrained::setPath(const Path *path)
const StaState *)
{ {
path_.init(path); path_.init(path);
crpr_valid_ = false; crpr_valid_ = false;

View File

@ -377,7 +377,7 @@ PathEnumFaninVisitor::makeDivertedPathEnd(Path *after_div,
path_enum_->makeDivertedPath(path_end_->path(), &before_div_, after_div, path_enum_->makeDivertedPath(path_end_->path(), &before_div_, after_div,
div_arc, div_path, after_div_copy); div_arc, div_path, after_div_copy);
div_end = path_end_->copy(); div_end = path_end_->copy();
div_end->setPath(div_path, this); div_end->setPath(div_path);
} }
void void

View File

@ -93,14 +93,13 @@ PathGroup::~PathGroup()
} }
bool bool
PathGroup::savable(PathEnd *path_end) PathGroup::saveable(PathEnd *path_end)
{ {
float threshold; float threshold;
{ {
LockGuard lock(lock_); LockGuard lock(lock_);
threshold = threshold_; threshold = threshold_;
} }
bool savable = false;
if (compare_slack_) { if (compare_slack_) {
// Crpr increases the slack, so check the slack // Crpr increases the slack, so check the slack
// without crpr first because it is expensive to find. // without crpr first because it is expensive to find.
@ -110,17 +109,49 @@ PathGroup::savable(PathEnd *path_end)
&& delayLessEqual(slack, slack_max_, sta_)) { && delayLessEqual(slack, slack_max_, sta_)) {
// Now check with crpr. // Now check with crpr.
slack = path_end->slack(sta_); slack = path_end->slack(sta_);
savable = delayLessEqual(slack, threshold, sta_) return delayLessEqual(slack, threshold, sta_)
&& delayLessEqual(slack, slack_max_, sta_) && delayLessEqual(slack, slack_max_, sta_)
&& delayGreaterEqual(slack, slack_min_, sta_); && delayGreaterEqual(slack, slack_min_, sta_);
} }
} }
else { else {
const Arrival &arrival = path_end->dataArrivalTime(sta_); const Arrival &arrival = path_end->dataArrivalTime(sta_);
savable = !delayIsInitValue(arrival, min_max_) return !delayIsInitValue(arrival, min_max_)
&& delayGreaterEqual(arrival, threshold, min_max_, sta_); && delayGreaterEqual(arrival, threshold, min_max_, sta_);
} }
return savable; return false;
}
// endpoint_path_count > 1 with slack_min requires
// saving endpoints with slack > slack_min so that
// path enumeration can find them. Use the patg end
// with the min(max) delay to prune ends that cannot
// onion peel down to slack_min.
bool
PathGroup::enumMinSlackUnderMin(PathEnd *path_end)
{
if (compare_slack_
&& endpoint_path_count_ > 1
&& slack_min_ > -INF) {
const Path *path = path_end->path();
PathAnalysisPt *other_ap = path->pathAnalysisPt(sta_)->tgtClkAnalysisPt();
const Tag *tag = path->tag(sta_);
VertexPathIterator other_iter(path->vertex(sta_),
path->transition(sta_),
other_ap, sta_);
while (other_iter.hasNext()) {
PathVertex *other = other_iter.next();
if (tagMatchCrpr(other->tag(sta_), tag)) {
PathEnd *end_min = path_end->copy();
end_min->setPath(other);
bool slack_under = fuzzyGreater(end_min->slackNoCrpr(sta_), slack_min_);
delete end_min;
if (slack_under)
return true;
}
}
}
return false;
} }
void void
@ -565,7 +596,7 @@ void
MakePathEnds1::visitPathEnd(PathEnd *path_end, MakePathEnds1::visitPathEnd(PathEnd *path_end,
PathGroup *group) PathGroup *group)
{ {
if (group->savable(path_end)) { if (group->saveable(path_end)) {
// Only keep the path end with the smallest slack/latest arrival. // Only keep the path end with the smallest slack/latest arrival.
PathEnd *worst_end = ends_.findKey(group); PathEnd *worst_end = ends_.findKey(group);
if (worst_end) { if (worst_end) {
@ -588,7 +619,7 @@ MakePathEnds1::vertexEnd(Vertex *)
PathGroup *group; PathGroup *group;
PathEnd *end; PathEnd *end;
group_iter.next(group, end); group_iter.next(group, end);
// visitPathEnd already confirmed slack is savable. // visitPathEnd already confirmed slack is saveable.
if (end) { if (end) {
group->insert(end); group->insert(end);
// Clear ends_ for next vertex. // Clear ends_ for next vertex.
@ -701,7 +732,8 @@ MakePathEndsAll::vertexEnd(Vertex *)
path_end->path()->tag(sta_)->index()); path_end->path()->tag(sta_)->index());
// Give the group a copy of the path end because // Give the group a copy of the path end because
// it may delete it during pruning. // it may delete it during pruning.
if (group->savable(path_end)) { if (group->saveable(path_end)
|| group->enumMinSlackUnderMin(path_end)) {
group->insert(path_end->copy()); group->insert(path_end->copy());
unique_ends.insert(path_end); unique_ends.insert(path_end);
n++; n++;
@ -788,7 +820,8 @@ PathGroups::enumPathEnds(PathGroup *group,
PathGroupIterator *end_iter = group->iterator(); PathGroupIterator *end_iter = group->iterator();
while (end_iter->hasNext()) { while (end_iter->hasNext()) {
PathEnd *end = end_iter->next(); PathEnd *end = end_iter->next();
if (group->savable(end)) if (group->saveable(end)
|| group->enumMinSlackUnderMin(end))
path_enum.insert(end); path_enum.insert(end);
} }
delete end_iter; delete end_iter;
@ -797,7 +830,10 @@ PathGroups::enumPathEnds(PathGroup *group,
// Parallel path enumeratation to find the endpoint_path_count/max path ends. // Parallel path enumeratation to find the endpoint_path_count/max path ends.
for (int n = 0; path_enum.hasNext() && n < group_path_count; n++) { for (int n = 0; path_enum.hasNext() && n < group_path_count; n++) {
PathEnd *end = path_enum.next(); PathEnd *end = path_enum.next();
group->insert(end); if (group->saveable(end))
group->insert(end);
else
delete end;
} }
} }

View File

@ -780,7 +780,7 @@ getProperty(const Port *port,
else if (stringEqual(property, "activity")) { else if (stringEqual(property, "activity")) {
const Instance *top_inst = network->topInstance(); const Instance *top_inst = network->topInstance();
const Pin *pin = network->findPin(top_inst, port); const Pin *pin = network->findPin(top_inst, port);
PwrActivity activity = sta->findClkedActivity(pin); PwrActivity activity = sta->activity(pin);
return PropertyValue(&activity); return PropertyValue(&activity);
} }
@ -998,7 +998,7 @@ getProperty(const Pin *pin,
return PropertyValue(&clks); return PropertyValue(&clks);
} }
else if (stringEqual(property, "activity")) { else if (stringEqual(property, "activity")) {
PwrActivity activity = sta->findClkedActivity(pin); PwrActivity activity = sta->activity(pin);
return PropertyValue(&activity); return PropertyValue(&activity);
} }

View File

@ -55,6 +55,15 @@
namespace sta { namespace sta {
static void
hierPinsAbove(const Net *net,
const Network *network,
PinSeq &pins_above);
static void
hierPinsAbove(const Pin *pin,
const Network *network,
PinSeq &pins_above);
static PinSeq static PinSeq
hierPinsThruEdge(const Edge *edge, hierPinsThruEdge(const Edge *edge,
const Network *network, const Network *network,
@ -326,14 +335,18 @@ void
ReportPath::reportPathEnds(PathEndSeq *ends) ReportPath::reportPathEnds(PathEndSeq *ends)
{ {
reportPathEndHeader(); reportPathEndHeader();
PathEndSeq::Iterator end_iter(ends); if (ends && !ends->empty()) {
PathEnd *prev_end = nullptr; PathEndSeq::Iterator end_iter(ends);
while (end_iter.hasNext()) { PathEnd *prev_end = nullptr;
PathEnd *end = end_iter.next(); while (end_iter.hasNext()) {
reportEndpointHeader(end, prev_end); PathEnd *end = end_iter.next();
end->reportFull(this); reportPathEnd(end, prev_end, !end_iter.hasNext());
reportBlankLine(); prev_end = end;
prev_end = end; }
}
else {
if (format_ != ReportPathFormat::json)
report_->reportLine("No paths found.");
} }
reportPathEndFooter(); reportPathEndFooter();
} }
@ -1086,9 +1099,9 @@ ReportPath::reportJson(const PathEnd *end,
const Pin *startpoint = expanded.startPath()->vertex(this)->pin(); const Pin *startpoint = expanded.startPath()->vertex(this)->pin();
const Pin *endpoint = expanded.endPath()->vertex(this)->pin(); const Pin *endpoint = expanded.endPath()->vertex(this)->pin();
stringAppend(result, " \"startpoint\": \"%s\",\n", stringAppend(result, " \"startpoint\": \"%s\",\n",
network_->pathName(startpoint)); sdc_network_->pathName(startpoint));
stringAppend(result, " \"endpoint\": \"%s\",\n", stringAppend(result, " \"endpoint\": \"%s\",\n",
network_->pathName(endpoint)); sdc_network_->pathName(endpoint));
const ClockEdge *src_clk_edge = end->sourceClkEdge(this); const ClockEdge *src_clk_edge = end->sourceClkEdge(this);
const PathVertex *tgt_clk_path = end->targetClkPath(); const PathVertex *tgt_clk_path = end->targetClkPath();
@ -1171,10 +1184,51 @@ ReportPath::reportJson(const PathExpanded &expanded,
for (size_t i = 0; i < expanded.size(); i++) { for (size_t i = 0; i < expanded.size(); i++) {
const PathRef *path = expanded.path(i); const PathRef *path = expanded.path(i);
const Pin *pin = path->vertex(this)->pin(); const Pin *pin = path->vertex(this)->pin();
const Net *net = network_->net(pin);
const Instance *inst = network_->instance(pin);
const RiseFall *rf = path->transition(this);
DcalcAnalysisPt *dcalc_ap = path->pathAnalysisPt(this)->dcalcAnalysisPt();
bool is_driver = network_->isDriver(pin);
stringAppend(result, "%*s {\n", indent, ""); stringAppend(result, "%*s {\n", indent, "");
if (inst) {
stringAppend(result, "%*s \"instance\": \"%s\",\n",
indent, "",
sdc_network_->pathName(inst));
Cell *cell = network_->cell(inst);
if (cell)
stringAppend(result, "%*s \"cell\": \"%s\",\n",
indent, "",
sdc_network_->name(cell));
stringAppend(result, "%*s \"verilog_src\": \"%s\",\n",
indent, "",
sdc_network_->getAttribute(inst, "src").c_str());
}
stringAppend(result, "%*s \"pin\": \"%s\",\n", stringAppend(result, "%*s \"pin\": \"%s\",\n",
indent, "", indent, "",
network_->pathName(pin)); sdc_network_->pathName(pin));
if (net) {
stringAppend(result, "%*s \"net\": \"%s\",\n",
indent, "",
sdc_network_->pathName(net));
}
PinSeq pins_above;
hierPinsAbove(pin, network_, pins_above);
if (!pins_above.empty()) {
stringAppend(result, "%*s \"hier_pins\": [\n", indent, "");
for (const Pin *hpin : pins_above) {
stringAppend(result, "%*s \"%s\"%s\n",
indent, "",
sdc_network_->pathName(hpin),
(hpin != pins_above.back()) ? "," : "");
}
stringAppend(result, "%*s ],\n", indent, "");
}
double x, y; double x, y;
bool exists; bool exists;
network_->location(pin, x, y, exists); network_->location(pin, x, y, exists);
@ -1186,6 +1240,10 @@ ReportPath::reportJson(const PathExpanded &expanded,
stringAppend(result, "%*s \"arrival\": %.3e,\n", stringAppend(result, "%*s \"arrival\": %.3e,\n",
indent, "", indent, "",
delayAsFloat(path->arrival(this))); delayAsFloat(path->arrival(this)));
if (is_driver)
stringAppend(result, "%*s \"capacitance\": %.3e,\n",
indent, "",
graph_delay_calc_->loadCap(pin, rf, dcalc_ap));
stringAppend(result, "%*s \"slew\": %.3e\n", stringAppend(result, "%*s \"slew\": %.3e\n",
indent, "", indent, "",
delayAsFloat(path->slew(this))); delayAsFloat(path->slew(this)));
@ -3481,15 +3539,6 @@ ReportPath::latchDesc(const RiseFall *clk_rf) const
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
static void
hierPinsAbove(const Net *net,
const Network *network,
PinSeq &pins_above);
static void
hierPinsAbove(const Pin *pin,
const Network *network,
PinSeq &pins_above);
static PinSeq static PinSeq
hierPinsThruEdge(const Edge *edge, hierPinsThruEdge(const Edge *edge,
const Network *network, const Network *network,
@ -3557,6 +3606,7 @@ hierPinsAbove(const Net *net,
if (hpin_net) if (hpin_net)
hierPinsAbove(hpin_net, network, pins_above); hierPinsAbove(hpin_net, network, pins_above);
} }
delete term_iter;
} }
} }

View File

@ -38,6 +38,7 @@ class ReportPath : public StaState
public: public:
explicit ReportPath(StaState *sta); explicit ReportPath(StaState *sta);
virtual ~ReportPath(); virtual ~ReportPath();
ReportPathFormat pathFormat() const { return format_; }
void setPathFormat(ReportPathFormat format); void setPathFormat(ReportPathFormat format);
void setReportFieldOrder(StringSeq *field_names); void setReportFieldOrder(StringSeq *field_names);
void setReportFields(bool report_input_pin, void setReportFields(bool report_input_pin,

View File

@ -509,6 +509,13 @@ report_path_cmd(PathRef *path)
Sta::sta()->reportPath(path); Sta::sta()->reportPath(path);
} }
void
report_path_ends(PathEndSeq *ends)
{
Sta::sta()->reportPathEnds(ends);
delete ends;
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
void void

View File

@ -420,14 +420,9 @@ define_cmd_args "report_checks" \
proc_redirect report_checks { proc_redirect report_checks {
global sta_report_unconstrained_paths global sta_report_unconstrained_paths
parse_report_path_options "report_checks" args "full" 0 parse_report_path_options "report_checks" args "full" 0
set path_ends [find_timing_paths_cmd "report_checks" args] set path_ends [find_timing_paths_cmd "report_checks" args]
if { $path_ends == {} } { report_path_ends $path_ends
report_line "No paths found."
} else {
report_path_ends $path_ends
}
} }
################################################################ ################################################################
@ -1091,19 +1086,6 @@ proc parse_path_group_arg { group_names } {
return $names return $names
} }
proc report_path_ends { path_ends } {
report_path_end_header
set prev_end "NULL"
set end_count [llength $path_ends]
set i 0
foreach path_end $path_ends {
report_path_end2 $path_end $prev_end [expr $i == ($end_count - 1)]
set prev_end $path_end
incr i
}
report_path_end_footer
}
################################################################ ################################################################
define_cmd_args "report_clock_min_period" \ define_cmd_args "report_clock_min_period" \

View File

@ -2555,6 +2555,12 @@ Sta::reportPathEnd(PathEnd *end,
report_path_->reportPathEnd(end, prev_end, last); report_path_->reportPathEnd(end, prev_end, last);
} }
void
Sta::reportPathEnds(PathEndSeq *ends)
{
report_path_->reportPathEnds(ends);
}
void void
Sta::reportPath(Path *path) Sta::reportPath(Path *path)
{ {
@ -5690,10 +5696,10 @@ Sta::power(const Instance *inst,
} }
PwrActivity PwrActivity
Sta::findClkedActivity(const Pin *pin) Sta::activity(const Pin *pin)
{ {
powerPreamble(); powerPreamble();
return power_->findClkedActivity(pin); return power_->pinActivity(pin);
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////

View File

@ -20,6 +20,13 @@
fprintf(stderr, "Error: out of memory.\n"); fprintf(stderr, "Error: out of memory.\n");
exit(1); exit(1);
} }
catch (ExceptionMsg &excp) {
if (!excp.suppressed()) {
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "Error: ", excp.what(), nullptr);
}
return TCL_ERROR;
}
catch (std::exception &excp) { catch (std::exception &excp) {
Tcl_ResetResult(interp); Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "Error: ", excp.what(), nullptr); Tcl_AppendResult(interp, "Error: ", excp.what(), nullptr);

View File

@ -39,22 +39,26 @@
#include "PathEnd.hh" #include "PathEnd.hh"
#include "SearchClass.hh" #include "SearchClass.hh"
#include "CircuitSim.hh" #include "CircuitSim.hh"
#include "ArcDelayCalc.hh"
#include "Property.hh" #include "Property.hh"
#include "Sta.hh" #include "Sta.hh"
#include "TclTypeHelpers.hh"
namespace sta { namespace sta {
typedef MinPulseWidthCheckSeq::Iterator MinPulseWidthCheckSeqIterator; typedef MinPulseWidthCheckSeq::Iterator MinPulseWidthCheckSeqIterator;
typedef MinMaxAll MinMaxAllNull; typedef MinMaxAll MinMaxAllNull;
#if TCL_MAJOR_VERSION < 9
typedef int Tcl_Size;
#endif
template <class TYPE> template <class TYPE>
Vector<TYPE> * Vector<TYPE> *
tclListSeqPtr(Tcl_Obj *const source, tclListSeqPtr(Tcl_Obj *const source,
swig_type_info *swig_type, swig_type_info *swig_type,
Tcl_Interp *interp) Tcl_Interp *interp)
{ {
int argc; Tcl_Size argc;
Tcl_Obj **argv; Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
@ -78,7 +82,7 @@ tclListSeq(Tcl_Obj *const source,
swig_type_info *swig_type, swig_type_info *swig_type,
Tcl_Interp *interp) Tcl_Interp *interp)
{ {
int argc; Tcl_Size argc;
Tcl_Obj **argv; Tcl_Obj **argv;
std::vector<TYPE> seq; std::vector<TYPE> seq;
@ -100,7 +104,7 @@ tclListSetPtr(Tcl_Obj *const source,
swig_type_info *swig_type, swig_type_info *swig_type,
Tcl_Interp *interp) Tcl_Interp *interp)
{ {
int argc; Tcl_Size argc;
Tcl_Obj **argv; Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
&& argc > 0) { && argc > 0) {
@ -123,7 +127,7 @@ tclListSet(Tcl_Obj *const source,
swig_type_info *swig_type, swig_type_info *swig_type,
Tcl_Interp *interp) Tcl_Interp *interp)
{ {
int argc; Tcl_Size argc;
Tcl_Obj **argv; Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
&& argc > 0) { && argc > 0) {
@ -147,7 +151,7 @@ tclListNetworkSet(Tcl_Obj *const source,
Tcl_Interp *interp, Tcl_Interp *interp,
const Network *network) const Network *network)
{ {
int argc; Tcl_Size argc;
Tcl_Obj **argv; Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
&& argc > 0) { && argc > 0) {
@ -171,7 +175,7 @@ tclListNetworkSet1(Tcl_Obj *const source,
Tcl_Interp *interp, Tcl_Interp *interp,
const Network *network) const Network *network)
{ {
int argc; Tcl_Size argc;
Tcl_Obj **argv; Tcl_Obj **argv;
SET_TYPE set(network); SET_TYPE set(network);
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
@ -186,68 +190,6 @@ tclListNetworkSet1(Tcl_Obj *const source,
return set; return set;
} }
static StringSet *
tclListSetConstChar(Tcl_Obj *const source,
Tcl_Interp *interp)
{
int argc;
Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) {
StringSet *set = new StringSet;
for (int i = 0; i < argc; i++) {
int length;
const char *str = Tcl_GetStringFromObj(argv[i], &length);
set->insert(str);
}
return set;
}
else
return nullptr;
}
static StringSeq *
tclListSeqConstChar(Tcl_Obj *const source,
Tcl_Interp *interp)
{
int argc;
Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) {
StringSeq *seq = new StringSeq;
for (int i = 0; i < argc; i++) {
int length;
const char *str = Tcl_GetStringFromObj(argv[i], &length);
seq->push_back(str);
}
return seq;
}
else
return nullptr;
}
#ifdef UNUSED
static StdStringSet *
tclListSetStdString(Tcl_Obj *const source,
Tcl_Interp *interp)
{
int argc;
Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) {
StdStringSet *set = new StdStringSet;
for (int i = 0; i < argc; i++) {
int length;
const char *str = Tcl_GetStringFromObj(argv[i], &length);
set->insert(str);
}
return set;
}
else
return nullptr;
}
#endif
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// Sequence out to tcl list. // Sequence out to tcl list.
@ -313,123 +255,6 @@ setPtrTclList(SET_TYPE *set,
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
static void
tclArgError(Tcl_Interp *interp,
const char *msg,
const char *arg)
{
// Swig does not add try/catch around arg parsing so this cannot use Report::error.
string error_msg = "Error: ";
error_msg += msg;
char *error = stringPrint(error_msg.c_str(), arg);
Tcl_SetResult(interp, error, TCL_VOLATILE);
stringDelete(error);
}
static void
objectListNext(const char *list,
const char *type,
// Return values.
bool &type_match,
const char *&next)
{
// Default return values (failure).
type_match = false;
next = nullptr;
// _hexaddress_p_type
const char *s = list;
char ch = *s++;
if (ch == '_') {
while (*s && isxdigit(*s))
s++;
if ((s - list - 1) == sizeof(void*) * 2
&& *s && *s++ == '_'
&& *s && *s++ == 'p'
&& *s && *s++ == '_') {
const char *t = type;
while (*s && *s != ' ') {
if (*s != *t)
return;
s++;
t++;
}
type_match = true;
if (*s)
next = s + 1;
else
next = nullptr;
}
}
}
#ifdef UNUSED
static Tcl_Obj *
tclArcDcalcArg(ArcDcalcArg &gate,
Tcl_Interp *interp)
{
Sta *sta = Sta::sta();
const Network *network = sta->network();
const Instance *drvr = network->instance(gate.drvrPin());
const TimingArc *arc = gate.arc();
Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
Tcl_Obj *obj;
const char *inst_name = network->pathName(drvr);
obj = Tcl_NewStringObj(inst_name, strlen(inst_name));
Tcl_ListObjAppendElement(interp, list, obj);
const char *from_name = arc->from()->name();
obj = Tcl_NewStringObj(from_name, strlen(from_name));
Tcl_ListObjAppendElement(interp, list, obj);
const char *from_edge = arc->fromEdge()->asString();
obj = Tcl_NewStringObj(from_edge, strlen(from_edge));
Tcl_ListObjAppendElement(interp, list, obj);
const char *to_name = arc->to()->name();
obj = Tcl_NewStringObj(to_name, strlen(to_name));
Tcl_ListObjAppendElement(interp, list, obj);
const char *to_edge = arc->toEdge()->asString();
obj = Tcl_NewStringObj(to_edge, strlen(to_edge));
Tcl_ListObjAppendElement(interp, list, obj);
const char *input_delay = delayAsString(gate.inputDelay(), sta, 3);
obj = Tcl_NewStringObj(input_delay, strlen(input_delay));
Tcl_ListObjAppendElement(interp, list, obj);
return list;
}
static ArcDcalcArg
arcDcalcArgTcl(Tcl_Obj *obj,
Tcl_Interp *interp)
{
Sta *sta = Sta::sta();
sta->ensureGraph();
int list_argc;
Tcl_Obj **list_argv;
if (Tcl_ListObjGetElements(interp, obj, &list_argc, &list_argv) == TCL_OK) {
const char *input_delay = "0.0";
int length;
if (list_argc == 6)
input_delay = Tcl_GetStringFromObj(list_argv[5], &length);
if (list_argc == 5 || list_argc == 6) {
return makeArcDcalcArg(Tcl_GetStringFromObj(list_argv[0], &length),
Tcl_GetStringFromObj(list_argv[1], &length),
Tcl_GetStringFromObj(list_argv[2], &length),
Tcl_GetStringFromObj(list_argv[3], &length),
Tcl_GetStringFromObj(list_argv[4], &length),
input_delay, sta);
}
else
sta->report()->warn(2140, "Delay calc arg requires 5 or 6 args.");
}
return ArcDcalcArg();
}
#endif
} // namespace } // namespace
using namespace sta; using namespace sta;
@ -913,7 +738,7 @@ using namespace sta;
} }
%typemap(in) FloatSeq* { %typemap(in) FloatSeq* {
int argc; Tcl_Size argc;
Tcl_Obj **argv; Tcl_Obj **argv;
FloatSeq *floats = nullptr; FloatSeq *floats = nullptr;
@ -958,7 +783,7 @@ using namespace sta;
} }
%typemap(in) IntSeq* { %typemap(in) IntSeq* {
int argc; Tcl_Size argc;
Tcl_Obj **argv; Tcl_Obj **argv;
IntSeq *ints = nullptr; IntSeq *ints = nullptr;
@ -1286,6 +1111,10 @@ using namespace sta;
Tcl_SetObjResult(interp, obj); Tcl_SetObjResult(interp, obj);
} }
%typemap(in) PathEndSeq* {
$1 = tclListSeqPtr<PathEnd*>($input, SWIGTYPE_p_PathEnd, interp);
}
%typemap(out) PathEndSeq* { %typemap(out) PathEndSeq* {
Tcl_Obj *list = Tcl_NewListObj(0, nullptr); Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
const PathEndSeq *path_ends = $1; const PathEndSeq *path_ends = $1;
@ -1541,7 +1370,7 @@ using namespace sta;
Tcl_Obj *obj; Tcl_Obj *obj;
const char *str; const char *str;
str = stringPrintTmp("%.5e", activity.activity()); str = stringPrintTmp("%.5e", activity.density());
obj = Tcl_NewStringObj(str, strlen(str)); obj = Tcl_NewStringObj(str, strlen(str));
Tcl_ListObjAppendElement(interp, list, obj); Tcl_ListObjAppendElement(interp, list, obj);
@ -1586,7 +1415,7 @@ using namespace sta;
%typemap(in) ArcDcalcArgSeq { %typemap(in) ArcDcalcArgSeq {
Tcl_Obj *const source = $input; Tcl_Obj *const source = $input;
int argc; Tcl_Size argc;
Tcl_Obj **argv; Tcl_Obj **argv;
Sta *sta = Sta::sta(); Sta *sta = Sta::sta();

201
tcl/TclTypeHelpers.cc Normal file
View File

@ -0,0 +1,201 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, 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/>.
#include "TclTypeHelpers.hh"
#include "Liberty.hh"
#include "Network.hh"
#include "Sta.hh"
namespace sta {
StringSet *
tclListSetConstChar(Tcl_Obj *const source,
Tcl_Interp *interp)
{
Tcl_Size argc;
Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) {
StringSet *set = new StringSet;
for (int i = 0; i < argc; i++) {
int length;
const char *str = Tcl_GetStringFromObj(argv[i], &length);
set->insert(str);
}
return set;
}
else
return nullptr;
}
StringSeq *
tclListSeqConstChar(Tcl_Obj *const source,
Tcl_Interp *interp)
{
Tcl_Size argc;
Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) {
StringSeq *seq = new StringSeq;
for (int i = 0; i < argc; i++) {
int length;
const char *str = Tcl_GetStringFromObj(argv[i], &length);
seq->push_back(str);
}
return seq;
}
else
return nullptr;
}
StdStringSet *
tclListSetStdString(Tcl_Obj *const source,
Tcl_Interp *interp)
{
Tcl_Size argc;
Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) {
StdStringSet *set = new StdStringSet;
for (int i = 0; i < argc; i++) {
int length;
const char *str = Tcl_GetStringFromObj(argv[i], &length);
set->insert(str);
}
return set;
}
else
return nullptr;
}
void
tclArgError(Tcl_Interp *interp,
const char *msg,
const char *arg)
{
// Swig does not add try/catch around arg parsing so this cannot use Report::error.
string error_msg = "Error: ";
error_msg += msg;
char *error = stringPrint(error_msg.c_str(), arg);
Tcl_SetResult(interp, error, TCL_VOLATILE);
stringDelete(error);
}
void
objectListNext(const char *list,
const char *type,
// Return values.
bool &type_match,
const char *&next)
{
// Default return values (failure).
type_match = false;
next = nullptr;
// _hexaddress_p_type
const char *s = list;
char ch = *s++;
if (ch == '_') {
while (*s && isxdigit(*s))
s++;
if ((s - list - 1) == sizeof(void*) * 2
&& *s && *s++ == '_'
&& *s && *s++ == 'p'
&& *s && *s++ == '_') {
const char *t = type;
while (*s && *s != ' ') {
if (*s != *t)
return;
s++;
t++;
}
type_match = true;
if (*s)
next = s + 1;
else
next = nullptr;
}
}
}
Tcl_Obj *
tclArcDcalcArg(ArcDcalcArg &gate,
Tcl_Interp *interp)
{
Sta *sta = Sta::sta();
const Network *network = sta->network();
const Instance *drvr = network->instance(gate.drvrPin());
const TimingArc *arc = gate.arc();
Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
Tcl_Obj *obj;
const char *inst_name = network->pathName(drvr);
obj = Tcl_NewStringObj(inst_name, strlen(inst_name));
Tcl_ListObjAppendElement(interp, list, obj);
const char *from_name = arc->from()->name();
obj = Tcl_NewStringObj(from_name, strlen(from_name));
Tcl_ListObjAppendElement(interp, list, obj);
const char *from_edge = arc->fromEdge()->asString();
obj = Tcl_NewStringObj(from_edge, strlen(from_edge));
Tcl_ListObjAppendElement(interp, list, obj);
const char *to_name = arc->to()->name();
obj = Tcl_NewStringObj(to_name, strlen(to_name));
Tcl_ListObjAppendElement(interp, list, obj);
const char *to_edge = arc->toEdge()->asString();
obj = Tcl_NewStringObj(to_edge, strlen(to_edge));
Tcl_ListObjAppendElement(interp, list, obj);
const char *input_delay = delayAsString(gate.inputDelay(), sta, 3);
obj = Tcl_NewStringObj(input_delay, strlen(input_delay));
Tcl_ListObjAppendElement(interp, list, obj);
return list;
}
ArcDcalcArg
arcDcalcArgTcl(Tcl_Obj *obj,
Tcl_Interp *interp)
{
Sta *sta = Sta::sta();
sta->ensureGraph();
int list_argc;
Tcl_Obj **list_argv;
if (Tcl_ListObjGetElements(interp, obj, &list_argc, &list_argv) == TCL_OK) {
const char *input_delay = "0.0";
int length;
if (list_argc == 6)
input_delay = Tcl_GetStringFromObj(list_argv[5], &length);
if (list_argc == 5 || list_argc == 6) {
return makeArcDcalcArg(Tcl_GetStringFromObj(list_argv[0], &length),
Tcl_GetStringFromObj(list_argv[1], &length),
Tcl_GetStringFromObj(list_argv[2], &length),
Tcl_GetStringFromObj(list_argv[3], &length),
Tcl_GetStringFromObj(list_argv[4], &length),
input_delay, sta);
}
else
sta->report()->warn(2140, "Delay calc arg requires 5 or 6 args.");
}
return ArcDcalcArg();
}
} // namespace

View File

@ -192,10 +192,13 @@ proc sta_warn { msg_id msg } {
proc sta_error { msg_id msg } { proc sta_error { msg_id msg } {
variable sdc_file variable sdc_file
variable sdc_line variable sdc_line
if { [info exists sdc_file] } {
error "Error: [file tail $sdc_file] line $sdc_line, $msg" if { ! [is_suppressed $msg_id] } {
} else { if { [info exists sdc_file] } {
error "Error: $msg" error "Error: [file tail $sdc_file] line $sdc_line, $msg"
} else {
error "Error: $msg"
}
} }
} }
@ -207,6 +210,24 @@ proc sta_warn_error { msg_id warn_error msg } {
} }
} }
define_cmd_args "suppress_msg" msg_ids
proc suppress_msg { args } {
foreach msg_id $args {
check_integer "msg_id" $msg_id
suppress_msg_id $msg_id
}
}
define_cmd_args "unsuppress_msg" msg_ids
proc unsuppress_msg { args } {
foreach msg_id $args {
check_integer "msg_id" $msg_id
unsuppress_msg_id $msg_id
}
}
# Defined by StaTcl.i # Defined by StaTcl.i
define_cmd_args "elapsed_run_time" {} define_cmd_args "elapsed_run_time" {}
define_cmd_args "user_run_time" {} define_cmd_args "user_run_time" {}

BIN
test/asap7_ccsn.lib.gz Normal file

Binary file not shown.

0
test/liberty_ccsn.ok Normal file
View File

2
test/liberty_ccsn.tcl Normal file
View File

@ -0,0 +1,2 @@
# read ccsn library and ensure no unnecessary warnings
read_liberty asap7_ccsn.lib.gz

View File

@ -2,11 +2,11 @@ Warning: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not foun
Group Internal Switching Leakage Total Group Internal Switching Leakage Total
Power Power Power Power (Watts) Power Power Power Power (Watts)
---------------------------------------------------------------- ----------------------------------------------------------------
Sequential 3.07e-04 4.75e-05 2.96e-10 3.54e-04 40.1% Sequential 3.07e-04 4.76e-05 2.96e-10 3.54e-04 40.0%
Combinational 1.58e-04 2.04e-04 6.86e-10 3.62e-04 41.0% Combinational 1.59e-04 2.05e-04 6.86e-10 3.64e-04 41.1%
Clock 4.68e-05 1.20e-04 2.30e-11 1.67e-04 18.9% Clock 4.68e-05 1.20e-04 2.30e-11 1.67e-04 18.9%
Macro 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0% Macro 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%
Pad 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0% Pad 0.00e+00 0.00e+00 0.00e+00 0.00e+00 0.0%
---------------------------------------------------------------- ----------------------------------------------------------------
Total 5.11e-04 3.72e-04 1.00e-09 8.84e-04 100.0% Total 5.12e-04 3.73e-04 1.00e-09 8.85e-04 100.0%
57.9% 42.1% 0.0% 57.8% 42.2% 0.0%

View File

@ -1,5 +1,7 @@
Warning: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11. Warning: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11.
Annotated 937 pin activities. Annotated 937 pin activities.
vcd 937
unannotated 0
Group Internal Switching Leakage Total Group Internal Switching Leakage Total
Power Power Power Power (Watts) Power Power Power Power (Watts)
---------------------------------------------------------------- ----------------------------------------------------------------

View File

@ -75,6 +75,9 @@ proc parse_args {} {
lappend app_options $threads lappend app_options $threads
set argv [lrange $argv 2 end] set argv [lrange $argv 2 end]
} elseif { $arg == "-valgrind" } { } elseif { $arg == "-valgrind" } {
if { ![find_valgrind] } {
error "valgrind not found."
}
set use_valgrind 1 set use_valgrind 1
set argv [lrange $argv 1 end] set argv [lrange $argv 1 end]
} elseif { $arg == "-report_stats" } { } elseif { $arg == "-report_stats" } {
@ -92,6 +95,18 @@ proc parse_args {} {
} }
} }
# Find valgrind in $PATH.
proc find_valgrind {} {
global env
foreach dir [regsub -all ":" $env(PATH) " "] {
if { [file executable [file join $dir "valgrind"]] } {
return 1
}
}
return 0
}
proc expand_tests { argv } { proc expand_tests { argv } {
global test_groups errors global test_groups errors

View File

@ -112,27 +112,31 @@ proc list_delete { list delete } {
# Record tests in sta/examples # Record tests in sta/examples
record_example_tests { record_example_tests {
sdf_delays
delay_calc delay_calc
min_max_delays min_max_delays
spef_parasitics
multi_corner multi_corner
power power
power_vcd power_vcd
sdf_delays
spef_parasitics
} }
record_sta_tests { record_sta_tests {
prima3
verilog_attribute
liberty_arcs_one2one_1
liberty_arcs_one2one_2
get_is_memory
get_filter get_filter
get_is_memory
get_lib_pins_of_objects
get_noargs get_noargs
get_objrefs get_objrefs
get_lib_pins_of_objects liberty_arcs_one2one_1
report_checks_src_attr liberty_arcs_one2one_2
liberty_ccsn
liberty_latch3 liberty_latch3
prima3
report_checks_src_attr
report_json1
report_json2
suppress_msg
verilog_attribute
} }
define_test_group fast [group_tests all] define_test_group fast [group_tests all]

View File

@ -1,5 +1,5 @@
Startpoint: in (input port clocked by clk) Startpoint: _1415_ (rising edge-triggered flip-flop clocked by clk)
Endpoint: _1415_ (rising edge-triggered flip-flop clocked by clk) Endpoint: _1416_[0] (rising edge-triggered flip-flop clocked by clk)
Path Group: clk Path Group: clk
Path Type: max Path Type: max
@ -7,22 +7,22 @@ Path Type: max
--------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------
0.00 0.00 0.00 clock clk (rise edge) 0.00 0.00 0.00 clock clk (rise edge)
0.00 0.00 clock network delay (ideal) 0.00 0.00 clock network delay (ideal)
0.00 0.00 v input external delay 0.00 0.00 0.00 ^ _1415_/CLK (sky130_fd_sc_hd__dfrtp_1) synthesis/tests/counter.v:22.3-28.6
0.00 0.00 0.00 0.00 v in (in) 0.00 0.04 0.33 0.33 v _1415_/Q (sky130_fd_sc_hd__dfrtp_1) synthesis/tests/counter.v:22.3-28.6
in (net) mid (net) synthesis/tests/counter.v:22.3-28.6
0.00 0.00 0.00 v _1415_/D (sky130_fd_sc_hd__dfrtp_1) synthesis/tests/counter.v:22.3-28.6 0.04 0.00 0.33 v _1416_[0]/D (sky130_fd_sc_hd__dfrtp_1) synthesis/tests/counter.v:22.3-28.6
0.00 data arrival time 0.33 data arrival time
0.00 10.00 10.00 clock clk (rise edge) 0.00 10.00 10.00 clock clk (rise edge)
0.00 10.00 clock network delay (ideal) 0.00 10.00 clock network delay (ideal)
0.00 10.00 clock reconvergence pessimism 0.00 10.00 clock reconvergence pessimism
10.00 ^ _1415_/CLK (sky130_fd_sc_hd__dfrtp_1) 10.00 ^ _1416_[0]/CLK (sky130_fd_sc_hd__dfrtp_1)
-0.10 9.90 library setup time -0.12 9.88 library setup time
9.90 data required time 9.88 data required time
--------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------
9.90 data required time 9.88 data required time
-0.00 data arrival time -0.33 data arrival time
--------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------
9.90 slack (MET) 9.55 slack (MET)

78
test/report_json1.ok Normal file
View File

@ -0,0 +1,78 @@
{"checks": [
{
"type": "check",
"path_group": "clk",
"path_type": "max",
"startpoint": "_1415_/Q",
"endpoint": "_1416_[0]/D",
"source_clock": "clk",
"source_clock_edge": "rise",
"source_path": [
{
"instance": "",
"cell": "counter",
"verilog_src": "",
"pin": "clk",
"arrival": 0.000e+00,
"capacitance": 3.742e-15,
"slew": 0.000e+00
},
{
"instance": "_1415_",
"cell": "sky130_fd_sc_hd__dfrtp_1",
"verilog_src": "synthesis/tests/counter.v:22.3-28.6",
"pin": "_1415_/CLK",
"net": "clk",
"arrival": 0.000e+00,
"slew": 0.000e+00
},
{
"instance": "_1415_",
"cell": "sky130_fd_sc_hd__dfrtp_1",
"verilog_src": "synthesis/tests/counter.v:22.3-28.6",
"pin": "_1415_/Q",
"net": "mid",
"arrival": 3.296e-10,
"capacitance": 1.949e-15,
"slew": 3.612e-11
},
{
"instance": "_1416_[0]",
"cell": "sky130_fd_sc_hd__dfrtp_1",
"verilog_src": "synthesis/tests/counter.v:22.3-28.6",
"pin": "_1416_[0]/D",
"net": "mid",
"arrival": 3.296e-10,
"slew": 3.612e-11
}
],
"target_clock": "clk",
"target_clock_edge": "rise",
"target_clock_path": [
{
"instance": "",
"cell": "counter",
"verilog_src": "",
"pin": "clk",
"arrival": 0.000e+00,
"capacitance": 3.742e-15,
"slew": 0.000e+00
},
{
"instance": "_1416_[0]",
"cell": "sky130_fd_sc_hd__dfrtp_1",
"verilog_src": "synthesis/tests/counter.v:22.3-28.6",
"pin": "_1416_[0]/CLK",
"net": "clk",
"arrival": 0.000e+00,
"slew": 0.000e+00
}
],
"data_arrival_time": 3.296e-10,
"crpr": 0.000e+00,
"margin": 1.207e-10,
"required_time": 9.879e-09,
"slack": 9.550e-09
}
]
}

7
test/report_json1.tcl Normal file
View File

@ -0,0 +1,7 @@
# report_checks -format json
read_liberty ../examples/sky130hd_tt.lib.gz
read_verilog verilog_attribute.v
link_design counter
create_clock -name clk -period 10 clk
set_input_delay -clock clk 0 [all_inputs -no_clocks]
report_checks -path_group clk -format json

3
test/report_json2.ok Normal file
View File

@ -0,0 +1,3 @@
{"checks": [
]
}

6
test/report_json2.tcl Normal file
View File

@ -0,0 +1,6 @@
# report_checks -format json with no paths
read_liberty ../examples/sky130hd_tt.lib.gz
read_verilog verilog_attribute.v
link_design counter
create_clock -name clk -period 10
report_checks -path_group clk -format json

12
test/suppress_msg.ok Normal file
View File

@ -0,0 +1,12 @@
Warning: suppress_msg.tcl line 18, cmd warn 1
caught Error: suppress_msg.tcl line 18, cmd error 1
Warning: cmd warn 2
caught Error: cmd error 2
after error
caught
caught
after error
Warning: suppress_msg.tcl line 51, cmd warn 7
caught Error: suppress_msg.tcl line 51, cmd error 7
Warning: cmd warn 8
caught Error: cmd error 8

56
test/suppress_msg.tcl Normal file
View File

@ -0,0 +1,56 @@
# suppress and unsuppress message ids
# Run sta_warn/sta_error to test TCL side suppression
proc sta_cmd { msg } {
sta::sta_warn 1 "cmd warn $msg"
sta::sta_error 2 "cmd error $msg"
puts "after error"
}
# Run report_warn/report_error to test C++ side suppression
proc report_cmd { msg } {
sta::report_warn 1 "cmd warn $msg"
sta::report_error 2 "cmd error $msg"
puts "after error"
}
# Ensure that TCL side messages are displayed as usual
catch { sta_cmd 1 } error
puts "caught $error"
# Ensure that C++ side messages are displayed as usual
catch { report_cmd 2 } error
puts "caught $error"
# Suppress messages
suppress_msg 1 2
# Ensure that TCL side messages are suppressed
catch { sta_cmd 3 } error
puts "caught $error"
# Ensure that C++ side messages are suppressed
catch { report_cmd 4 } error
puts "caught $error"
# Continue on error to avoid having to catch
set sta_continue_on_error 1
# Ensure that TCL side messages are suppressed
# TCL side will make it to "after error"
sta_cmd 5
# Ensure that C++ side messages are suppressed
# C++ will not make it to "after error" as the whole cmd is cancelled
report_cmd 6
# Unsuppress messages
unsuppress_msg 1 2
# Ensure that TCL side messages are displayed as usual
catch { sta_cmd 7 } error
puts "caught $error"
# Ensure that C++ side messages are displayed as usual
catch { report_cmd 8 } error
puts "caught $error"

View File

@ -11,11 +11,19 @@ module counter(clk, reset, in, out);
(* src = "synthesis/tests/counter.v:18.14-18.19" *) (* src = "synthesis/tests/counter.v:18.14-18.19" *)
input reset; input reset;
input in; input in;
wire mid;
(* bottom_bound = 1'sh0 *) (* bottom_bound = 1'sh0 *)
(* src = "synthesis/tests/counter.v:22.3-28.6", attr1 = "test_attr1", attr2 = "test_attr2" *) (* src = "synthesis/tests/counter.v:22.3-28.6", attr1 = "test_attr1", attr2 = "test_attr2" *)
sky130_fd_sc_hd__dfrtp_1 _1415_ ( sky130_fd_sc_hd__dfrtp_1 _1415_ (
.CLK(clk), .CLK(clk),
.D(in), .D(in),
.Q(mid),
.RESET_B(reset)
);
(* src = "synthesis/tests/counter.v:22.3-28.6" *)
sky130_fd_sc_hd__dfrtp_1 \_1416_[0] (
.CLK(clk),
.D(mid),
.Q(out), .Q(out),
.RESET_B(reset) .RESET_B(reset)
); );

View File

@ -28,9 +28,11 @@ Exception::Exception() :
{ {
} }
ExceptionMsg::ExceptionMsg(const char *msg) : ExceptionMsg::ExceptionMsg(const char *msg,
const bool suppressed) :
Exception(), Exception(),
msg_(msg) msg_(msg),
suppressed_(suppressed)
{ {
} }

View File

@ -169,59 +169,71 @@ Report::printBufferLine()
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
void void
Report::warn(int /* id */, Report::warn(int id,
const char *fmt, const char *fmt,
...) ...)
{ {
va_list args; // Skip suppressed messages.
va_start(args, fmt); if (!isSuppressed(id)) {
printToBuffer("Warning: "); va_list args;
printToBufferAppend(fmt, args); va_start(args, fmt);
printBufferLine(); printToBuffer("Warning: ");
va_end(args); printToBufferAppend(fmt, args);
printBufferLine();
va_end(args);
}
} }
void void
Report::vwarn(int /* id */, Report::vwarn(int id,
const char *fmt, const char *fmt,
va_list args) va_list args)
{ {
printToBuffer("Warning: "); // Skip suppressed messages.
printToBufferAppend(fmt, args); if (!isSuppressed(id)) {
printBufferLine(); printToBuffer("Warning: ");
printToBufferAppend(fmt, args);
printBufferLine();
}
} }
void void
Report::fileWarn(int /* id */, Report::fileWarn(int id,
const char *filename, const char *filename,
int line, int line,
const char *fmt, const char *fmt,
...) ...)
{ {
va_list args; // Skip suppressed messages.
va_start(args, fmt); if (!isSuppressed(id)) {
printToBuffer("Warning: %s line %d, ", filename, line); va_list args;
printToBufferAppend(fmt, args); va_start(args, fmt);
printBufferLine(); printToBuffer("Warning: %s line %d, ", filename, line);
va_end(args); printToBufferAppend(fmt, args);
printBufferLine();
va_end(args);
}
} }
void void
Report::vfileWarn(int /* id */, Report::vfileWarn(int id,
const char *filename, const char *filename,
int line, int line,
const char *fmt, const char *fmt,
va_list args) va_list args)
{ {
printToBuffer("Warning: %s line %d, ", filename, line); // Skip suppressed messages.
printToBufferAppend(fmt, args); if (!isSuppressed(id)) {
printBufferLine(); printToBuffer("Warning: %s line %d, ", filename, line);
printToBufferAppend(fmt, args);
printBufferLine();
}
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
void void
Report::error(int /* id */, Report::error(int id,
const char *fmt, ...) const char *fmt, ...)
{ {
va_list args; va_list args;
@ -229,21 +241,21 @@ Report::error(int /* id */,
// No prefix msg, no \n. // No prefix msg, no \n.
printToBuffer(fmt, args); printToBuffer(fmt, args);
va_end(args); va_end(args);
throw ExceptionMsg(buffer_); throw ExceptionMsg(buffer_, isSuppressed(id));
} }
void void
Report::verror(int /* id */, Report::verror(int id,
const char *fmt, const char *fmt,
va_list args) va_list args)
{ {
// No prefix msg, no \n. // No prefix msg, no \n.
printToBuffer(fmt, args); printToBuffer(fmt, args);
throw ExceptionMsg(buffer_); throw ExceptionMsg(buffer_, isSuppressed(id));
} }
void void
Report::fileError(int /* id */, Report::fileError(int id,
const char *filename, const char *filename,
int line, int line,
const char *fmt, const char *fmt,
@ -255,11 +267,11 @@ Report::fileError(int /* id */,
printToBuffer("%s line %d, ", filename, line); printToBuffer("%s line %d, ", filename, line);
printToBufferAppend(fmt, args); printToBufferAppend(fmt, args);
va_end(args); va_end(args);
throw ExceptionMsg(buffer_); throw ExceptionMsg(buffer_, isSuppressed(id));
} }
void void
Report::vfileError(int /* id */, Report::vfileError(int id,
const char *filename, const char *filename,
int line, int line,
const char *fmt, const char *fmt,
@ -268,7 +280,7 @@ Report::vfileError(int /* id */,
// No prefix msg, no \n. // No prefix msg, no \n.
printToBuffer("%s line %d, ", filename, line); printToBuffer("%s line %d, ", filename, line);
printToBufferAppend(fmt, args); printToBufferAppend(fmt, args);
throw ExceptionMsg(buffer_); throw ExceptionMsg(buffer_, isSuppressed(id));
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
@ -304,6 +316,26 @@ Report::fileCritical(int /* id */,
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
void
Report::suppressMsgId(int id)
{
suppressed_msg_ids_.insert(id);
}
void
Report::unsuppressMsgId(int id)
{
suppressed_msg_ids_.erase(id);
}
bool
Report::isSuppressed(int id)
{
return suppressed_msg_ids_.find(id) != suppressed_msg_ids_.end();
}
////////////////////////////////////////////////////////////////
void void
Report::logBegin(const char *filename) Report::logBegin(const char *filename)
{ {

View File

@ -31,14 +31,16 @@ using ::Tcl_GetChannelType;
extern "C" { extern "C" {
#if TCL_MAJOR_VERSION >= 9
#define CONST84 const
#endif
static int static int
encapOutputProc(ClientData instanceData, encapOutputProc(ClientData instanceData,
CONST84 char *buf, CONST84 char *buf,
int toWrite, int toWrite,
int *errorCodePtr); int *errorCodePtr);
static int static int
encapCloseProc(ClientData instanceData, Tcl_Interp *interp);
static int
encapSetOptionProc(ClientData instanceData, encapSetOptionProc(ClientData instanceData,
Tcl_Interp *interp, Tcl_Interp *interp,
CONST84 char *optionName, CONST84 char *optionName,
@ -53,11 +55,6 @@ encapInputProc(ClientData instanceData,
char *buf, char *buf,
int bufSize, int bufSize,
int *errorCodePtr); int *errorCodePtr);
static int
encapSeekProc(ClientData instanceData,
long offset,
int seekMode,
int *errorCodePtr);
static void static void
encapWatchProc(ClientData instanceData, int mask); encapWatchProc(ClientData instanceData, int mask);
static int static int
@ -66,36 +63,55 @@ encapGetHandleProc(ClientData instanceData,
ClientData *handlePtr); ClientData *handlePtr);
static int static int
encapBlockModeProc(ClientData instanceData, int mode); encapBlockModeProc(ClientData instanceData, int mode);
#if TCL_MAJOR_VERSION < 9
static int
encapCloseProc(ClientData instanceData, Tcl_Interp *interp);
static int
encapSeekProc(ClientData instanceData,
long offset,
int seekMode,
int *errorCodePtr);
#endif
} // extern "C" } // extern "C"
Tcl_ChannelType tcl_encap_type_stdout = { Tcl_ChannelType tcl_encap_type_stdout = {
const_cast<char *>("file"), const_cast<char*>("file"),
TCL_CHANNEL_VERSION_4, TCL_CHANNEL_VERSION_5,
encapCloseProc, #if TCL_MAJOR_VERSION < 9
encapInputProc, encapCloseProc,
encapOutputProc, #else
encapSeekProc, nullptr, // closeProc unused
encapSetOptionProc, #endif
encapGetOptionProc, encapInputProc,
encapWatchProc, encapOutputProc,
encapGetHandleProc, #if TCL_MAJOR_VERSION < 9
nullptr, // close2Proc encapSeekProc,
encapBlockModeProc, #else
nullptr, // flushProc nullptr, // close2Proc
nullptr, // handlerProc #endif
nullptr, // wideSeekProc encapSetOptionProc,
nullptr, // threadActionProc encapGetOptionProc,
nullptr // truncateProc encapWatchProc,
encapGetHandleProc,
nullptr, // close2Proc
encapBlockModeProc,
nullptr, // flushProc
nullptr, // handlerProc
nullptr, // wideSeekProc
nullptr, // threadActionProc
nullptr // truncateProc
}; };
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
ReportTcl::ReportTcl() : ReportTcl::ReportTcl() :
Report(), interp_(nullptr), Report(), interp_(nullptr),
tcl_stdout_(nullptr), tcl_stdout_(nullptr),
tcl_stderr_(nullptr), tcl_stderr_(nullptr),
tcl_encap_stdout_(nullptr), tcl_encap_stdout_(nullptr),
tcl_encap_stderr_(nullptr) tcl_encap_stderr_(nullptr)
{ {
} }
@ -228,17 +244,6 @@ encapInputProc(ClientData,
return -1; return -1;
} }
static int
encapCloseProc(ClientData instanceData,
Tcl_Interp *)
{
ReportTcl *report = reinterpret_cast<ReportTcl *>(instanceData);
report->logEnd();
report->redirectFileEnd();
report->redirectStringEnd();
return 0;
}
static int static int
encapSetOptionProc(ClientData, encapSetOptionProc(ClientData,
Tcl_Interp *, Tcl_Interp *,
@ -257,15 +262,6 @@ encapGetOptionProc(ClientData,
return 0; return 0;
} }
static int
encapSeekProc(ClientData,
long,
int,
int *)
{
return -1;
}
static void static void
encapWatchProc(ClientData, int) encapWatchProc(ClientData, int)
{ {
@ -286,4 +282,28 @@ encapBlockModeProc(ClientData,
return 0; return 0;
} }
#if TCL_MAJOR_VERSION < 9
static int
encapCloseProc(ClientData instanceData,
Tcl_Interp *)
{
ReportTcl *report = reinterpret_cast<ReportTcl *>(instanceData);
report->logEnd();
report->redirectFileEnd();
report->redirectStringEnd();
return 0;
}
static int
encapSeekProc(ClientData,
long,
int,
int *)
{
return -1;
}
#endif
} // namespace sta } // namespace sta

View File

@ -148,6 +148,24 @@ report_line(const char *msg)
printf("%s\n", msg); printf("%s\n", msg);
} }
void
suppress_msg_id(int id)
{
Sta::sta()->report()->suppressMsgId(id);
}
void
unsuppress_msg_id(int id)
{
Sta::sta()->report()->unsuppressMsgId(id);
}
int
is_suppressed(int id)
{
return Sta::sta()->report()->isSuppressed(id);
}
void void
fflush() fflush()
{ {

View File

@ -1822,7 +1822,7 @@ VerilogReader::linkNetwork(const char *top_cell_name,
VerilogModule *module = this->module(top_cell); VerilogModule *module = this->module(top_cell);
if (module) { if (module) {
// Seed the recursion for expansion with the top level instance. // Seed the recursion for expansion with the top level instance.
Instance *top_instance = network_->makeInstance(top_cell, "", nullptr); Instance *top_instance = network_->makeInstance(top_cell, top_cell_name, nullptr);
VerilogBindingTbl bindings(zero_net_name_, one_net_name_); VerilogBindingTbl bindings(zero_net_name_, one_net_name_);
VerilogNetSeq::Iterator port_iter(module->ports()); VerilogNetSeq::Iterator port_iter(module->ports());
while (port_iter.hasNext()) { while (port_iter.hasNext()) {