commit
95826f1eb9
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 && \
|
||||||
23
README.md
23
README.md
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
................
|
................
|
||||||
|
|
||||||
|
|
|
||||||
BIN
doc/OpenSTA.odt
BIN
doc/OpenSTA.odt
Binary file not shown.
BIN
doc/OpenSTA.pdf
BIN
doc/OpenSTA.pdf
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.
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
|
|
|
||||||
|
|
@ -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 \
|
||||||
|
|
|
||||||
346
power/Power.cc
346
power/Power.cc
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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] {
|
||||||
|
|
|
||||||
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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_;
|
||||||
|
|
|
||||||
213
power/Vcd.cc
213
power/Vcd.cc
|
|
@ -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_;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
158
power/Vcd.hh
158
power/Vcd.hh
|
|
@ -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
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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" \
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
29
tcl/Util.tcl
29
tcl/Util.tcl
|
|
@ -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" {}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,2 @@
|
||||||
|
# read ccsn library and ensure no unnecessary warnings
|
||||||
|
read_liberty asap7_ccsn.lib.gz
|
||||||
|
|
@ -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%
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{"checks": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -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)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
18
util/Util.i
18
util/Util.i
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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()) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue