Merge remote-tracking branch 'parallaxsw/master'

Signed-off-by: Eder Monteiro <emrmonteiro@precisioninno.com>
This commit is contained in:
Eder Monteiro 2025-01-21 08:50:02 -03:00
commit 32365d9bc4
75 changed files with 2133 additions and 62140 deletions

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# 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)
# Use standard target names
cmake_policy(SET CMP0078 NEW)
@ -125,6 +125,11 @@ set(STA_SOURCE
parasitics/SpefNamespace.cc
parasitics/SpefReader.cc
parasitics/SpefReaderPvt.hh
power/Power.cc
power/VcdReader.cc
power/SaifReader.cc
power/VcdParse.cc
sdc/Clock.cc
sdc/ClockGatingCheck.cc
@ -198,11 +203,7 @@ set(STA_SOURCE
spice/WriteSpice.cc
spice/Xyce.cc
power/Power.cc
power/ReadVcdActivities.cc
power/SaifReader.cc
power/Vcd.cc
power/VcdReader.cc
tcl/TclTypeHelpers.cc
util/Debug.cc
util/DispatchQueue.cc

View File

@ -11,7 +11,7 @@ RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo \
RUN sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo \
&& 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 \
&& 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
# Download CUDD

View File

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

View File

@ -94,7 +94,7 @@ work, but these are the versions used for development.
cmake 3.24.2 3.29.2
clang 15.0.0
gcc 11.4.0
tcl 8.6 8.6.6
tcl 8.6 8.6.16
swig 4.1.0 4.1.1
bison 3.8.2 3.8.2
flex 2.6.4 2.6.4
@ -105,7 +105,7 @@ are illegal in c++17.
External library dependencies:
```
Ubuntu Macos license
Ubuntu Darwin License
eigen 3.4.0 3.4.0 MPL2 required
cudd 3.0.0 3.0.0 BSD required
tclreadline 2.3.8 2.3.8 BSD optional
@ -197,7 +197,7 @@ following command builds a Docker image.
```
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
@ -237,7 +237,22 @@ case directory.
* 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

View File

@ -22,7 +22,8 @@
# because there doesn't appear to be a way to override
# searching OSX system directories before unix directories.
set(TCL_POSSIBLE_NAMES tcl87 tcl8.7
set(TCL_POSSIBLE_NAMES
tcl87 tcl8.7
tcl86 tcl8.6
tcl85 tcl8.5
)
@ -30,7 +31,7 @@ set(TCL_POSSIBLE_NAMES tcl87 tcl8.7
# tcl lib path guesses.
if (NOT TCL_LIB_PATHS)
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)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(TCL_LIB_PATHS /usr/lib /usr/lib64 /usr/local/lib)

View File

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

View File

@ -31,11 +31,14 @@ is now supported by the the read_saif command.
read_saif [-scope scope] filename
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.
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
-------------------------
@ -62,6 +65,11 @@ clocks from the list.
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
-------------------------

View File

@ -97,15 +97,15 @@ private order.
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
pair if the lookup fails. Use map::find or sta::Map::findKey instead.
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
................

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -2,11 +2,11 @@
read_liberty sky130hd_tt.lib.gz
read_verilog gcd_sky130hd.v
link_design gcd
read_sdc gcd_sky130hd.sdc
read_spef gcd_sky130hd.spef
# Generate vcd file
# iverilog -o gcd_tb gcd_tb.v
# 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,61 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "ArcDelayCalc.hh"
#include "StringSet.hh"
#include "StringSeq.hh"
#include <tcl.h>
namespace sta {
#if TCL_MAJOR_VERSION < 9
typedef int Tcl_Size;
#endif
StringSet *
tclListSetConstChar(Tcl_Obj *const source,
Tcl_Interp *interp);
StringSeq *
tclListSeqConstChar(Tcl_Obj *const source,
Tcl_Interp *interp);
StdStringSet *
tclListSetStdString(Tcl_Obj *const source,
Tcl_Interp *interp);
void
tclArgError(Tcl_Interp *interp,
const char *msg,
const char *arg);
void
objectListNext(const char *list,
const char *type,
// Return values.
bool &type_match,
const char *&next);
Tcl_Obj *
tclArcDcalcArg(ArcDcalcArg &gate,
Tcl_Interp *interp);
ArcDcalcArg
arcDcalcArgTcl(Tcl_Obj *obj,
Tcl_Interp *interp);
} // namespace

View File

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

View File

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

View File

@ -482,6 +482,10 @@ public:
void visitDriverWaveformRiseFall(LibertyAttr *attr,
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.
virtual void beginGroup1(LibertyGroup *) {}
virtual void beginGroup2(LibertyGroup *) {}
@ -520,7 +524,8 @@ protected:
LibraryAttrVisitor visitor);
void parseNames(const char *name_str);
void clearAxisValues();
void makeTableAxis(int index);
void makeTableAxis(int index,
LibertyAttr *attr);
StringSeq *parseNameList(const char *name_list);
StdStringSeq parseTokenList(const char *token_str,
@ -629,6 +634,7 @@ protected:
StringSeq bus_names_;
bool in_bus_;
bool in_bundle_;
bool in_ccsn_;
TableAxisVariable axis_var_[3];
FloatSeq *axis_values_[3];
int type_bit_from_;

View File

@ -78,7 +78,7 @@ proc_redirect read_spef {
$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 {
parse_key_args "report_parasitic_annotation" args \

View File

@ -59,13 +59,12 @@
// input_voltage : default_VDD_VSS_input;
// pin
// output_voltage : default_VDD_VSS_output;
//
// transition_density = activity / clock_period
namespace sta {
using std::abs;
using std::max;
using std::min;
using std::isnormal;
static bool
@ -87,8 +86,8 @@ static EnumNameMap<PwrActivityOrigin> pwr_activity_origin_map =
Power::Power(StaState *sta) :
StaState(sta),
global_activity_{0.0, 0.0, PwrActivityOrigin::unknown},
input_activity_{0.1, 0.5, PwrActivityOrigin::input},
global_activity_(),
input_activity_(), // default set in ensureActivities()
seq_activity_map_(100, SeqPinHash(network_), SeqPinEqual()),
activities_valid_(false),
bdd_(sta)
@ -96,41 +95,41 @@ Power::Power(StaState *sta) :
}
void
Power::setGlobalActivity(float activity,
Power::setGlobalActivity(float density,
float duty)
{
global_activity_.set(activity, duty, PwrActivityOrigin::global);
global_activity_.set(density, duty, PwrActivityOrigin::global);
activities_valid_ = false;
}
void
Power::setInputActivity(float activity,
Power::setInputActivity(float density,
float duty)
{
input_activity_.set(activity, duty, PwrActivityOrigin::input);
input_activity_.set(density, duty, PwrActivityOrigin::input);
activities_valid_ = false;
}
void
Power::setInputPortActivity(const Port *input_port,
float activity,
float density,
float duty)
{
Instance *top_inst = network_->topInstance();
const Pin *pin = network_->findPin(top_inst, input_port);
if (pin) {
user_activity_map_[pin] = {activity, duty, PwrActivityOrigin::user};
user_activity_map_[pin] = {density, duty, PwrActivityOrigin::user};
activities_valid_ = false;
}
}
void
Power::setUserActivity(const Pin *pin,
float activity,
float density,
float duty,
PwrActivityOrigin origin)
{
user_activity_map_[pin] = {activity, duty, origin};
user_activity_map_[pin] = {density, duty, origin};
activities_valid_ = false;
}
@ -152,7 +151,7 @@ Power::setActivity(const Pin *pin,
{
debugPrint(debug_, "power_activity", 3, "set %s %.2e %.2f %s",
network_->pathName(pin),
activity.activity(),
activity.density(),
activity.duty(),
pwr_activity_origin_map.find(activity.origin()));
activity_map_[pin] = activity;
@ -403,7 +402,7 @@ PropActivityVisitor::visit(Vertex *vertex)
Vertex *from_vertex = edge->from(graph_);
const Pin *from_pin = from_vertex->pin();
PwrActivity &from_activity = power_->activity(from_pin);
PwrActivity to_activity(from_activity.activity(),
PwrActivity to_activity(from_activity.density(),
from_activity.duty(),
PwrActivityOrigin::propagated);
changed = setActivityCheck(pin, to_activity);
@ -426,13 +425,13 @@ PropActivityVisitor::visit(Vertex *vertex)
PwrActivity activity2 = power_->findActivity(enable);
float p1 = activity1.duty();
float p2 = activity2.duty();
PwrActivity activity(activity1.activity() * p2 + activity2.activity() * p1,
PwrActivity activity(activity1.density() * p2 + activity2.density() * p1,
p1 * p2,
PwrActivityOrigin::propagated);
changed = setActivityCheck(gclk, activity);
debugPrint(debug_, "power_activity", 3, "gated_clk %s %.2e %.2f",
network_->pathName(gclk),
activity.activity(),
activity.density(),
activity.duty());
}
}
@ -466,13 +465,17 @@ bool
PropActivityVisitor::setActivityCheck(const Pin *pin,
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);
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());
if (activity_delta > change_tolerance_
if (density_delta > change_tolerance_
|| duty_delta > change_tolerance_
|| 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);
power_->setActivity(pin, activity);
return true;
@ -517,11 +520,11 @@ Power::evalActivity(FuncExpr *expr,
else {
DdNode *bdd = bdd_.funcBdd(expr);
float duty = evalBddDuty(bdd, inst);
float activity = evalBddActivity(bdd, inst);
float density = evalBddActivity(bdd, inst);
Cudd_RecursiveDeref(bdd_.cuddMgr(), bdd);
bdd_.clearVarMap();
return PwrActivity(activity, duty, PwrActivityOrigin::propagated);
return PwrActivity(density, duty, PwrActivityOrigin::propagated);
}
}
@ -588,7 +591,7 @@ float
Power::evalBddActivity(DdNode *bdd,
const Instance *inst)
{
float activity = 0.0;
float density = 0.0;
for (const auto [port, var_node] : bdd_.portVarMap()) {
const Pin *pin = findLinkPin(inst, port);
if (pin) {
@ -598,21 +601,16 @@ Power::evalBddActivity(DdNode *bdd,
Cudd_Ref(diff);
float diff_duty = evalBddDuty(diff, inst);
Cudd_RecursiveDeref(bdd_.cuddMgr(), diff);
float var_act = var_activity.activity() * diff_duty;
activity += var_act;
if (debug_->check("power_activity", 3)) {
const Clock *clk = findClk(pin);
float clk_period = clk ? clk->period() : 1.0;
debugPrint(debug_, "power_activity", 3, "var %s%s %.3e * %.3f = %.3e",
port->name(),
clk ? "" : " (unclocked)",
var_activity.activity() / clk_period,
diff_duty,
var_act / clk_period);
}
float var_density = var_activity.density() * diff_duty;
density += var_density;
debugPrint(debug_, "power_activity", 3, "var %s %.3e * %.3f = %.3e",
port->name(),
var_activity.density(),
diff_duty,
var_density);
}
}
return activity;
return density;
}
////////////////////////////////////////////////////////////////
@ -628,6 +626,13 @@ Power::ensureActivities()
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);
BfsFwdIterator bfs(BfsIndex::other, &activity_srch_pred, this);
seedActivities(bfs);
@ -722,9 +727,17 @@ Power::seedRegOutputActivities(const Instance *reg,
PwrActivity activity = evalActivity(seq->data(), reg);
// Register output activity cannnot exceed one transition per clock cycle,
// but latch output can.
if (seq->isRegister()
&& activity.activity() > 1.0)
activity.setActivity(1.0);
if (seq->isRegister()) {
FuncExpr *clk_func = seq->clock();
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)
activity.setDuty(1.0 - activity.duty());
activity.setOrigin(PwrActivityOrigin::propagated);
@ -740,9 +753,8 @@ Power::power(const Instance *inst,
const Corner *corner)
{
PowerResult result;
const Clock *inst_clk = findInstClk(inst);
findInternalPower(inst, cell, corner, inst_clk, result);
findSwitchingPower(inst, cell, corner, inst_clk, result);
findInternalPower(inst, cell, corner, result);
findSwitchingPower(inst, cell, corner, result);
findLeakagePower(inst, cell, corner, result);
return result;
}
@ -768,7 +780,6 @@ void
Power::findInternalPower(const Instance *inst,
LibertyCell *cell,
const Corner *corner,
const Clock *inst_clk,
// Return values.
PowerResult &result)
{
@ -781,7 +792,7 @@ Power::findInternalPower(const Instance *inst,
float load_cap = to_port->direction()->isAnyOutput()
? graph_delay_calc_->loadCap(to_pin, dcalc_ap)
: 0.0;
PwrActivity activity = findClkedActivity(to_pin, inst_clk);
PwrActivity activity = findActivity(to_pin);
if (to_port->direction()->isAnyOutput())
findOutputInternalPower(to_port, inst, cell, activity,
load_cap, corner, result);
@ -850,18 +861,18 @@ Power::findInputInternalPower(const Pin *pin,
else
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",
port->name(),
when ? when->asString() : "",
activity.activity() * 1e-9,
activity.density() * 1e-9,
duty,
energy,
port_internal,
related_pg_pin ? related_pg_pin : "no pg_pin");
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()));
}
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 *
Power::findExprOutPort(FuncExpr *expr)
{
@ -936,11 +968,11 @@ Power::findOutputInternalPower(const LibertyPort *to_port,
const LibertyPort *from_corner_port = pwr->relatedPort();
if (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);
const char *related_pg_pin = pwr->relatedPgPin();
// 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()) {
float duty_sum = duty_sum_iter->second;
if (duty_sum != 0.0 && from_pin) {
float from_activity = findActivity(from_pin).activity();
weight = from_activity * duty / duty_sum;
float from_density = findActivity(from_pin).density();
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",
from_corner_port ? from_corner_port->name() : "-" ,
to_port->name(),
when ? when->asString() : "",
to_activity.activity() * 1e-9,
to_activity.density() * 1e-9,
duty,
weight,
energy,
@ -999,7 +1031,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port,
related_pg_pin ? related_pg_pin : "no pg_pin");
internal += port_internal;
}
result.internal() += internal;
result.incrInternal(internal);
}
float
@ -1023,7 +1055,7 @@ Power::findInputDuty(const Instance *inst,
else if (when)
return evalActivity(when, inst).duty();
else if (search_->isClock(from_vertex))
return 1.0;
return 0.5;
return 0.5;
}
}
@ -1068,7 +1100,6 @@ void
Power::findSwitchingPower(const Instance *inst,
LibertyCell *cell,
const Corner *corner,
const Clock *inst_clk,
// Return values.
PowerResult &result)
{
@ -1082,17 +1113,17 @@ Power::findSwitchingPower(const Instance *inst,
float load_cap = to_port->direction()->isAnyOutput()
? graph_delay_calc_->loadCap(to_pin, dcalc_ap)
: 0.0;
PwrActivity activity = findClkedActivity(to_pin, inst_clk);
PwrActivity activity = findActivity(to_pin);
if (to_port->direction()->isAnyOutput()) {
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",
cell->name(),
to_port->name(),
activity.activity(),
activity.density(),
volt,
switching);
result.switching() += switching;
result.incrSwitching(switching);
}
}
}
@ -1160,34 +1191,15 @@ Power::findLeakagePower(const Instance *inst,
debugPrint(debug_, "power", 2, "leakage %s %.3e",
cell->name(),
leakage);
result.leakage() += leakage;
result.incrLeakage(leakage);
}
// External.
PwrActivity
Power::findClkedActivity(const Pin *pin)
Power::pinActivity(const Pin *pin)
{
const Instance *inst = network_->instance(pin);
const Clock *inst_clk = findInstClk(inst);
ensureActivities();
return findClkedActivity(pin, inst_clk);
}
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;
return findActivity(pin);
}
PwrActivity
@ -1204,7 +1216,7 @@ Power::findActivity(const Pin *pin)
}
const Clock *clk = findClk(pin);
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())
return global_activity_;
@ -1243,10 +1255,9 @@ Power::findSeqActivity(const Instance *inst,
return global_activity_;
else if (hasSeqActivity(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
@ -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() :
internal_(0.0),
switching_(0.0),
@ -1326,6 +1462,24 @@ PowerResult::total() const
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
PowerResult::incr(PowerResult &result)
{
@ -1336,17 +1490,17 @@ PowerResult::incr(PowerResult &result)
////////////////////////////////////////////////////////////////
PwrActivity::PwrActivity(float activity,
PwrActivity::PwrActivity(float density,
float duty,
PwrActivityOrigin origin) :
activity_(activity),
density_(density),
duty_(duty),
origin_(origin)
{
}
PwrActivity::PwrActivity() :
activity_(0.0),
density_(0.0),
duty_(0.0),
origin_(PwrActivityOrigin::unknown)
{
@ -1354,9 +1508,9 @@ PwrActivity::PwrActivity() :
}
void
PwrActivity::setActivity(float activity)
PwrActivity::setDensity(float density)
{
activity_ = activity;
density_ = density;
}
void
@ -1372,11 +1526,11 @@ PwrActivity::setOrigin(PwrActivityOrigin origin)
}
void
PwrActivity::set(float activity,
PwrActivity::set(float density,
float duty,
PwrActivityOrigin origin)
{
activity_ = activity;
density_ = density;
duty_ = duty;
origin_ = origin;
check();
@ -1385,11 +1539,11 @@ PwrActivity::set(float activity,
void
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
// point anomalies.
if (abs(activity_) < min_activity)
activity_ = 0.0;
if (abs(density_) < min_density)
density_ = 0.0;
}
bool

View File

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

View File

@ -18,9 +18,9 @@
%{
#include "Sta.hh"
#include "Sdc.hh"
#include "power/Power.hh"
#include "power/VcdReader.hh"
#include "power/ReadVcdActivities.hh"
#include "power/SaifReader.hh"
using namespace sta;
@ -102,6 +102,13 @@ set_power_pin_activity(const Pin *pin,
return power->setUserActivity(pin, activity, duty, PwrActivityOrigin::user);
}
float
clock_min_period()
{
Power *power = Sta::sta()->power();
return power->clockMinPeriod();
}
////////////////////////////////////////////////////////////////
void
@ -113,22 +120,6 @@ read_vcd_file(const char *filename,
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
@ -140,4 +131,13 @@ read_saif_file(const char *filename,
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

View File

@ -217,46 +217,67 @@ define_cmd_args "set_power_activity" { [-global]\
[-input]\
[-input_ports ports]\
[-pins pins]\
[-activity activity]\
[-duty duty] }
[-activity activity | -density density]\
[-duty duty]\
[-clock clock]}
proc 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}
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)] } {
set activity $keys(-activity)
check_float "activity" $activity
if { $activity < 0.0 } {
sta_warn 301 "activity should be 0.0 to 1.0 or 2.0"
check_positive_float "activity" $activity
if { [info exists keys(-clock)] } {
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
if { [info exists keys(-duty)] } {
set duty $keys(-duty)
check_float "duty" $duty
if { $duty < 0.0 || $duty > 1.0 } {
sta_warn 302 "duty should be 0.0 to 1.0"
if { $duty < 0.0 || $duty > 1.0 } {i
sta_error 302 "duty should be 0.0 to 1.0"
}
}
if { [info exists flags(-global)] } {
set_power_global_activity $activity $duty
set_power_global_activity $density $duty
}
if { [info exists flags(-input)] } {
set_power_input_activity $activity $duty
set_power_input_activity $density $duty
}
if { [info exists keys(-input_ports)] } {
set ports [get_ports_error "input_ports" $keys(-input_ports)]
foreach port $ports {
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."
} 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)] } {
set pins [get_pins $keys(-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 { } {
set corner [cmd_corner]
foreach inst [network_leaf_instances] {

View File

@ -1,258 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "ReadVcdActivities.hh"
#include <inttypes.h>
#include <set>
#include "VcdReader.hh"
#include "Debug.hh"
#include "Network.hh"
#include "VerilogNamespace.hh"
#include "ParseBus.hh"
#include "Sdc.hh"
#include "Power.hh"
#include "Sta.hh"
namespace sta {
using std::abs;
using std::min;
using std::to_string;
class ReadVcdActivities : public StaState
{
public:
ReadVcdActivities(const char *filename,
const char *scope,
Sta *sta);
void readActivities();
private:
void setActivities();
void setVarActivity(VcdVar *var,
string &var_name,
const VcdValues &var_value);
void setVarActivity(const char *pin_name,
const VcdValues &var_values,
int value_bit);
void findVarActivity(const VcdValues &var_values,
int value_bit,
// Return values.
double &transition_count,
double &activity,
double &duty);
void checkClkPeriod(const Pin *pin,
double transition_count);
const char *filename_;
const char *scope_;
Vcd vcd_;
double clk_period_;
Sta *sta_;
Power *power_;
std::set<const Pin*> annotated_pins_;
static constexpr double sim_clk_period_tolerance_ = .1;
};
void
readVcdActivities(const char *filename,
const char *scope,
Sta *sta)
{
ReadVcdActivities reader(filename, scope, sta);
reader.readActivities();
}
ReadVcdActivities::ReadVcdActivities(const char *filename,
const char *scope,
Sta *sta) :
StaState(sta),
filename_(filename),
scope_(scope),
vcd_(sta),
clk_period_(0.0),
sta_(sta),
power_(sta->power())
{
}
void
ReadVcdActivities::readActivities()
{
vcd_ = readVcdFile(filename_, sta_);
clk_period_ = INF;
for (Clock *clk : *sta_->sdc()->clocks())
clk_period_ = min(static_cast<double>(clk->period()), clk_period_);
if (vcd_.timeMax() > 0)
setActivities();
else
report_->warn(1450, "VCD max time is zero.");
report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size());
}
void
ReadVcdActivities::setActivities()
{
size_t scope_length = strlen(scope_);
for (VcdVar *var : vcd_.vars()) {
const VcdValues &var_values = vcd_.values(var);
if (!var_values.empty()
&& (var->type() == VcdVarType::wire
|| var->type() == VcdVarType::reg)) {
string var_name = var->name();
// string::starts_with in c++20
if (scope_length) {
if (var_name.substr(0, scope_length) == scope_) {
var_name = var_name.substr(scope_length + 1);
setVarActivity(var, var_name, var_values);
}
}
else
setVarActivity(var, var_name, var_values);
}
}
}
void
ReadVcdActivities::setVarActivity(VcdVar *var,
string &var_name,
const VcdValues &var_values)
{
if (var->width() == 1) {
string sta_name = netVerilogToSta(var_name.c_str());
setVarActivity(sta_name.c_str(), var_values, 0);
}
else {
bool is_bus, is_range, subscript_wild;
string bus_name;
int from, to;
parseBusName(var_name.c_str(), '[', ']', '\\',
is_bus, is_range, bus_name, from, to, subscript_wild);
if (is_bus) {
string sta_bus_name = netVerilogToSta(bus_name.c_str());
int value_bit = 0;
if (to < from) {
for (int bus_bit = to; bus_bit <= from; bus_bit++) {
string pin_name = sta_bus_name;
pin_name += '[';
pin_name += to_string(bus_bit);
pin_name += ']';
setVarActivity(pin_name.c_str(), var_values, value_bit);
value_bit++;
}
}
else {
for (int bus_bit = to; bus_bit >= from; bus_bit--) {
string pin_name = sta_bus_name;
pin_name += '[';
pin_name += to_string(bus_bit);
pin_name += ']';
setVarActivity(pin_name.c_str(), var_values, value_bit);
value_bit++;
}
}
}
else
report_->warn(1451, "problem parsing bus %s.", var_name.c_str());
}
}
void
ReadVcdActivities::setVarActivity(const char *pin_name,
const VcdValues &var_values,
int value_bit)
{
const Pin *pin = sdc_network_->findPin(pin_name);
if (pin) {
debugPrint(debug_, "read_vcd_activities", 3, "%s values", pin_name);
double transition_count, activity, duty;
findVarActivity(var_values, value_bit,
transition_count, activity, duty);
debugPrint(debug_, "read_vcd_activities", 1,
"%s transitions %.1f activity %.2f duty %.2f",
pin_name,
transition_count,
activity,
duty);
if (sdc_->isLeafPinClock(pin))
checkClkPeriod(pin, transition_count);
power_->setUserActivity(pin, activity, duty, PwrActivityOrigin::vcd);
annotated_pins_.insert(pin);
}
}
void
ReadVcdActivities::findVarActivity(const VcdValues &var_values,
int value_bit,
// Return values.
double &transition_count,
double &activity,
double &duty)
{
transition_count = 0.0;
char prev_value = var_values[0].value(value_bit);
VcdTime prev_time = var_values[0].time();
VcdTime high_time = 0;
for (const VcdValue &var_value : var_values) {
VcdTime time = var_value.time();
char value = var_value.value(value_bit);
debugPrint(debug_, "read_vcd_activities", 3, " %" PRId64 " %c", time, value);
if (prev_value == '1')
high_time += time - prev_time;
if (value != prev_value)
transition_count += (value == 'X'
|| value == 'Z'
|| prev_value == 'X'
|| prev_value == 'Z')
? .5
: 1.0;
prev_time = time;
prev_value = value;
}
VcdTime time_max = vcd_.timeMax();
if (prev_value == '1')
high_time += time_max - prev_time;
duty = static_cast<double>(high_time) / time_max;
activity = transition_count / (time_max * vcd_.timeScale() / clk_period_);
}
void
ReadVcdActivities::checkClkPeriod(const Pin *pin,
double transition_count)
{
VcdTime time_max = vcd_.timeMax();
double sim_period = time_max * vcd_.timeScale() / (transition_count / 2.0);
ClockSet *clks = sdc_->findLeafPinClocks(pin);
if (clks) {
for (Clock *clk : *clks) {
double clk_period = clk->period();
if (abs((clk_period - sim_period) / clk_period) > .1)
// Warn if sim clock period differs from SDC by 10%.
report_->warn(1452, "clock %s vcd period %s differs from SDC clock period %s",
clk->name(),
delayAsString(sim_period, this),
delayAsString(clk_period, this));
}
}
}
}

View File

@ -1,28 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
namespace sta {
class Sta;
void
readVcdActivities(const char *filename,
const char *scope,
Sta *sta);
} // namespace

View File

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

View File

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

View File

@ -1,213 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Vcd.hh"
#include "Report.hh"
namespace sta {
Vcd::Vcd(StaState *sta) :
StaState(sta),
time_scale_(1.0),
time_unit_scale_(1.0),
max_var_name_length_(0),
max_var_width_(0),
min_delta_time_(0),
time_max_(0)
{
}
Vcd::Vcd(const Vcd &vcd) :
StaState(vcd),
date_(vcd.date_),
comment_(vcd.comment_),
version_(vcd.version_),
time_scale_(vcd.time_scale_),
time_unit_(vcd.time_unit_),
time_unit_scale_(vcd.time_unit_scale_),
vars_(vcd.vars_),
var_name_map_(vcd.var_name_map_),
max_var_name_length_(vcd.max_var_name_length_),
max_var_width_(vcd.max_var_width_),
id_values_map_(vcd.id_values_map_),
min_delta_time_(vcd.min_delta_time_),
time_max_(vcd.time_max_)
{
}
Vcd&
Vcd::operator=(Vcd &&vcd1)
{
date_ = vcd1.date_;
comment_ = vcd1.comment_;
version_ = vcd1.version_;
time_scale_ = vcd1.time_scale_;
time_unit_ = vcd1.time_unit_;
time_unit_scale_ = vcd1.time_unit_scale_;
vars_ = vcd1.vars_;
var_name_map_ = vcd1.var_name_map_;
max_var_name_length_ = vcd1.max_var_name_length_;
max_var_width_ = vcd1.max_var_width_;
id_values_map_ = vcd1.id_values_map_;
min_delta_time_ = vcd1.min_delta_time_;
time_max_ = vcd1.time_max_;
vcd1.vars_.clear();
return *this;
}
Vcd::~Vcd()
{
for (VcdVar *var : vars_)
delete var;
}
void
Vcd::setTimeUnit(const string &time_unit,
double time_unit_scale)
{
time_unit_ = time_unit;
time_unit_scale_ = time_unit_scale;
}
void
Vcd::setDate(const string &date)
{
date_ = date;
}
void
Vcd::setComment(const string &comment)
{
comment_ = comment;
}
void
Vcd::setVersion(const string &version)
{
version_ = version;
}
void
Vcd::setTimeScale(double time_scale)
{
time_scale_ = time_scale;
}
void
Vcd::setMinDeltaTime(VcdTime min_delta_time)
{
min_delta_time_ = min_delta_time;
}
void
Vcd::setTimeMax(VcdTime time_max)
{
time_max_ = time_max;
}
void
Vcd::makeVar(string &name,
VcdVarType type,
int width,
string &id)
{
VcdVar *var = new VcdVar(name, type, width, id);
vars_.push_back(var);
var_name_map_[name] = var;
max_var_name_length_ = std::max(max_var_name_length_, name.size());
max_var_width_ = std::max(max_var_width_, width);
// Make entry for var ID.
id_values_map_[id].clear();
}
VcdVar *
Vcd::var(const string name)
{
return var_name_map_[name];
}
bool
Vcd::varIdValid(string &id)
{
return id_values_map_.find(id) != id_values_map_.end();
}
void
Vcd::varAppendValue(string &id,
VcdTime time,
char value)
{
VcdValues &values = id_values_map_[id];
values.emplace_back(time, value, 0);
}
void
Vcd::varAppendBusValue(string &id,
VcdTime time,
int64_t bus_value)
{
VcdValues &values = id_values_map_[id];
values.emplace_back(time, '\0', bus_value);
}
VcdValues &
Vcd::values(VcdVar *var)
{
if (id_values_map_.find(var->id()) == id_values_map_.end()) {
report_->error(1360, "Unknown variable %s ID %s",
var->name().c_str(),
var->id().c_str());
static VcdValues empty;
return empty;
}
else
return id_values_map_[var->id()];
}
////////////////////////////////////////////////////////////////
VcdVar::VcdVar(string name,
VcdVarType type,
int width,
string id) :
name_(name),
type_(type),
width_(width),
id_(id)
{
}
VcdValue::VcdValue(VcdTime time,
char value,
uint64_t bus_value) :
time_(time),
value_(value),
bus_value_(bus_value)
{
}
char
VcdValue::value(int value_bit) const
{
if (value_ == '\0')
return ((bus_value_ >> value_bit) & 0x1) ? '1' : '0';
else
return value_;
}
}

View File

@ -1,158 +0,0 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include "StaState.hh"
namespace sta {
using std::string;
using std::vector;
using std::map;
using std::max;
using std::min;
class VcdVar;
class VcdValue;
typedef vector<VcdValue> VcdValues;
typedef int64_t VcdTime;
typedef vector<string> VcdScope;
typedef map<string, VcdVar*> VcdNameMap;
enum class VcdVarType {
wire,
reg,
parameter,
integer,
real,
supply0,
supply1,
time,
tri,
triand,
trior,
trireg,
tri0,
tri1,
wand,
wor,
unknown
};
class Vcd : public StaState
{
public:
Vcd(StaState *sta);
Vcd(const Vcd &vcd);
// Move copy assignment.
Vcd& operator=(Vcd &&vcd1);
~Vcd();
VcdVar *var(const string name);
VcdValues &values(VcdVar *var);
const string &date() const { return date_; }
void setDate(const string &date);
const string &comment() const { return comment_; }
void setComment(const string &comment);
const string &version() const { return version_; }
void setVersion(const string &version);
double timeScale() const { return time_scale_; }
void setTimeScale(double time_scale);
const string &timeUnit() const { return time_unit_; }
double timeUnitScale() const { return time_unit_scale_; }
void setTimeUnit(const string &time_unit,
double time_unit_scale);
VcdTime timeMax() const { return time_max_; }
void setTimeMax(VcdTime time_max);
VcdTime minDeltaTime() const { return min_delta_time_; }
void setMinDeltaTime(VcdTime min_delta_time);
vector<VcdVar*> vars() { return vars_; }
void makeVar(string &name,
VcdVarType type,
int width,
string &id);
int maxVarWidth() const { return max_var_width_; }
int maxVarNameLength() const { return max_var_name_length_; }
bool varIdValid(string &id);
void varAppendValue(string &id,
VcdTime time,
char value);
void varAppendBusValue(string &id,
VcdTime time,
int64_t bus_value);
private:
string date_;
string comment_;
string version_;
double time_scale_;
string time_unit_;
double time_unit_scale_;
vector<VcdVar*> vars_;
VcdNameMap var_name_map_;
size_t max_var_name_length_;
int max_var_width_;
map<string, VcdValues> id_values_map_;
VcdTime min_delta_time_;
VcdTime time_max_;
};
class VcdVar
{
public:
VcdVar(string name,
VcdVarType type,
int width,
string id);
const string& name() const { return name_; }
VcdVarType type() const { return type_; }
int width() const { return width_; }
const string& id() const { return id_; }
private:
string name_;
VcdVarType type_;
int width_;
string id_;
};
class VcdValue
{
public:
VcdValue(VcdTime time,
char value,
uint64_t bus_value);
VcdTime time() const { return time_; }
char value() const { return value_; }
uint64_t busValue() const { return bus_value_; }
char value(int value_bit) const;
private:
VcdTime time_;
// 01XUZ or '\0' when width > 1 to use bus_value_.
char value_;
uint64_t bus_value_;
};
} // namespace

318
power/VcdParse.cc Normal file
View File

@ -0,0 +1,318 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "VcdParse.hh"
#include <cctype>
#include <cinttypes>
#include "Stats.hh"
#include "Report.hh"
#include "Error.hh"
#include "EnumNameMap.hh"
namespace sta {
using std::isspace;
// Very imprecise syntax definition
// https://en.wikipedia.org/wiki/Value_change_dump#Structure.2FSyntax
// Much better syntax definition
// https://web.archive.org/web/20120323132708/http://www.beyondttl.com/vcd.php
void
VcdParse::read(const char *filename,
VcdReader *reader)
{
stream_ = gzopen(filename, "r");
if (stream_) {
Stats stats(debug_, report_);
filename_ = filename;
reader_ = reader;
file_line_ = 1;
stmt_line_ = 1;
string token = getToken();
while (!token.empty()) {
if (token == "$date")
reader_->setDate(readStmtString());
else if (token == "$comment")
reader_->setComment(readStmtString());
else if (token == "$version")
reader_->setVersion(readStmtString());
else if (token == "$timescale")
parseTimescale();
else if (token == "$var")
parseVar();
else if (token == "$scope")
parseScope();
else if (token == "$upscope")
parseUpscope();
else if (token == "$enddefinitions")
// empty body
readStmtString();
else if (token == "$dumpall")
parseVarValues();
else if (token == "$dumpvars")
// Initial values.
parseVarValues();
else if (token[0] == '$')
report_->fileError(800, filename_, stmt_line_, "unhandled vcd command.");
else
parseVarValues();
token = getToken();
}
gzclose(stream_);
stats.report("Read VCD");
}
else
throw FileNotReadable(filename);
}
VcdParse::VcdParse(Report *report,
Debug *debug) :
reader_(nullptr),
file_line_(0),
stmt_line_(0),
time_(0),
prev_time_(0),
report_(report),
debug_(debug)
{
}
void
VcdParse::parseTimescale()
{
vector<string> tokens = readStmtTokens();
if (tokens.size() == 1) {
size_t last;
double time_scale = std::stod(tokens[0], &last);
setTimeUnit(tokens[0].substr(last), time_scale);
}
else if (tokens.size() == 2) {
double time_scale = std::stod(tokens[0]);
setTimeUnit(tokens[1], time_scale);
}
else
report_->fileError(801, filename_, stmt_line_, "timescale syntax error.");
}
void
VcdParse::setTimeUnit(const string &time_unit,
double time_scale)
{
double time_unit_scale = 1.0;
if (time_unit == "fs")
time_unit_scale = 1e-15;
else if (time_unit == "ps")
time_unit_scale = 1e-12;
else if (time_unit == "ns")
time_unit_scale = 1e-9;
else
report_->fileError(802, filename_, stmt_line_, "Unknown timescale unit.");
reader_->setTimeUnit(time_unit, time_unit_scale, time_scale);
}
static EnumNameMap<VcdVarType> vcd_var_type_map =
{{VcdVarType::wire, "wire"},
{VcdVarType::reg, "reg"},
{VcdVarType::parameter, "parameter"},
{VcdVarType::integer, "integer"},
{VcdVarType::real, "real"},
{VcdVarType::supply0, "supply0"},
{VcdVarType::supply1, "supply1"},
{VcdVarType::time, "time"},
{VcdVarType::tri, "tri"},
{VcdVarType::triand, "triand"},
{VcdVarType::trior, "trior"},
{VcdVarType::trireg, "trireg"},
{VcdVarType::tri0, "tri0"},
{VcdVarType::tri1, "tri1"},
{VcdVarType::wand, "wand"},
{VcdVarType::wor, "wor"}
};
void
VcdParse::parseVar()
{
vector<string> tokens = readStmtTokens();
if (tokens.size() == 4
|| tokens.size() == 5) {
string type_name = tokens[0];
VcdVarType type = vcd_var_type_map.find(type_name, VcdVarType::unknown);
if (type == VcdVarType::unknown)
report_->fileWarn(1370, filename_, stmt_line_,
"Unknown variable type %s.",
type_name.c_str());
else {
size_t width = stoi(tokens[1]);
string &id = tokens[2];
string name = tokens[3];
// iverilog separates bus base name from bit range.
if (tokens.size() == 5) {
// Preserve space after esacaped name.
if (name[0] == '\\')
name += ' ';
name += tokens[4];
}
reader_->makeVar(scope_, name, type, width, id);
}
}
else
report_->fileError(804, filename_, stmt_line_, "Variable syntax error.");
}
void
VcdParse::parseScope()
{
vector<string> tokens = readStmtTokens();
string &scope = tokens[1];
scope_.push_back(scope);
}
void
VcdParse::parseUpscope()
{
readStmtTokens();
scope_.pop_back();
}
void
VcdParse::parseVarValues()
{
string token = getToken();
while (!token.empty()) {
char char0 = toupper(token[0]);
if (char0 == '#' && token.size() > 1) {
prev_time_ = time_;
time_ = stoll(token.substr(1));
if (time_ > prev_time_)
reader_->varMinDeltaTime(time_ - prev_time_);
}
else if (char0 == '0'
|| char0 == '1'
|| char0 == 'X'
|| char0 == 'U'
|| char0 == 'Z') {
string id = token.substr(1);
if (!reader_->varIdValid(id))
report_->fileError(805, filename_, stmt_line_,
"unknown variable %s", id.c_str());
reader_->varAppendValue(id, time_, char0);
}
else if (char0 == 'B') {
char char1 = toupper(token[1]);
if (char1 == 'X'
|| char1 == 'U'
|| char1 == 'Z') {
string id = getToken();
if (!reader_->varIdValid(id))
report_->fileError(806, filename_, stmt_line_,
"unknown variable %s", id.c_str());
// Bus mixed 0/1/X/U not supported.
reader_->varAppendValue(id, time_, char1);
}
else {
string bin = token.substr(1);
char *end;
int64_t bus_value = strtol(bin.c_str(), &end, 2);
string id = getToken();
if (!reader_->varIdValid(id))
report_->fileError(807, filename_, stmt_line_,
"unknown variable %s", id.c_str());
else
reader_->varAppendBusValue(id, time_, bus_value);
}
}
token = getToken();
}
reader_->setTimeMax(time_);
}
string
VcdParse::readStmtString()
{
stmt_line_ = file_line_;
string line;
string token = getToken();
while (!token.empty() && token != "$end") {
if (!line.empty())
line += " ";
line += token;
token = getToken();
}
return line;
}
vector<string>
VcdParse::readStmtTokens()
{
stmt_line_ = file_line_;
vector<string> tokens;
string token = getToken();
while (!token.empty() && token != "$end") {
tokens.push_back(token);
token = getToken();
}
return tokens;
}
string
VcdParse::getToken()
{
string token;
int ch = gzgetc(stream_);
if (ch == '\n')
file_line_++;
// skip whitespace
while (ch != EOF && isspace(ch)) {
ch = gzgetc(stream_);
if (ch == '\n')
file_line_++;
}
while (ch != EOF && !isspace(ch)) {
token.push_back(ch);
ch = gzgetc(stream_);
if (ch == '\n')
file_line_++;
}
if (ch == EOF)
return "";
else
return token;
}
////////////////////////////////////////////////////////////////
char
VcdValue::value(int value_bit) const
{
if (value_ == '\0')
return ((bus_value_ >> value_bit) & 0x1) ? '1' : '0';
else
return value_;
}
void
VcdValue::setValue(VcdTime time,
char value)
{
time_ = time;
value_ = value;
}
} // namespace

136
power/VcdParse.hh Normal file
View File

@ -0,0 +1,136 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include "Zlib.hh"
#include "StaState.hh"
namespace sta {
using std::string;
using std::vector;
typedef int64_t VcdTime;
typedef vector<string> VcdScope;
enum class VcdVarType {
wire,
reg,
parameter,
integer,
real,
supply0,
supply1,
time,
tri,
triand,
trior,
trireg,
tri0,
tri1,
wand,
wor,
unknown
};
class VcdReader;
class VcdParse : public StaState
{
public:
VcdParse(Report *report,
Debug *debug);
void read(const char *filename,
VcdReader *reader);
private:
void parseTimescale();
void setTimeUnit(const string &time_unit,
double time_scale);
void parseVar();
void parseScope();
void parseUpscope();
void parseVarValues();
string getToken();
string readStmtString();
vector<string> readStmtTokens();
VcdReader *reader_;
gzFile stream_;
string token_;
const char *filename_;
int file_line_;
int stmt_line_;
VcdTime time_;
VcdTime prev_time_;
VcdScope scope_;
Report *report_;
Debug *debug_;
};
// Abstract class for VcdParse callbacks.
class VcdReader
{
public:
virtual ~VcdReader() {}
virtual void setDate(const string &date) = 0;
virtual void setComment(const string &comment) = 0;
virtual void setVersion(const string &version) = 0;
virtual void setTimeUnit(const string &time_unit,
double time_unit_scale,
double time_scale) = 0;
virtual void setTimeMax(VcdTime time_max) = 0;
virtual void varMinDeltaTime(VcdTime min_delta_time) = 0;
virtual bool varIdValid(const string &id) = 0;
virtual void makeVar(const VcdScope &scope,
const string &name,
VcdVarType type,
size_t width,
const string &id) = 0;
virtual void varAppendValue(const string &id,
VcdTime time,
char value) = 0;
virtual void varAppendBusValue(const string &id,
VcdTime time,
int64_t bus_value) = 0;
};
class VcdValue
{
public:
VcdValue();
VcdTime time() const { return time_; }
char value() const { return value_; }
void setValue(VcdTime time,
char value);
uint64_t busValue() const { return bus_value_; }
char value(int value_bit) const;
private:
VcdTime time_;
// 01XUZ or '\0' when width > 1 to use bus_value_.
char value_;
uint64_t bus_value_;
};
} // namespace

View File

@ -14,437 +14,428 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cctype>
#include <cinttypes>
#include "VcdReader.hh"
#include "Zlib.hh"
#include "Stats.hh"
#include "Report.hh"
#include "Error.hh"
#include "StringUtil.hh"
#include "EnumNameMap.hh"
#include <inttypes.h>
#include <set>
#include "VcdParse.hh"
#include "Debug.hh"
#include "Network.hh"
#include "PortDirection.hh"
#include "VerilogNamespace.hh"
#include "ParseBus.hh"
#include "Sdc.hh"
#include "Power.hh"
#include "Sta.hh"
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
// 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
class VcdReader : public StaState
// Transition count and high time for duty cycle for a group of pins
// for one bit of vcd ID.
class VcdCount
{
public:
VcdReader(StaState *sta);
Vcd read(const char *filename);
VcdCount();
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:
void parseTimescale();
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_;
PinSeq pins_;
VcdTime prev_time_;
VcdScope scope_;
char prev_value_;
VcdTime high_time_;
double transition_count_;
};
Vcd
readVcdFile(const char *filename,
StaState *sta)
VcdCount::VcdCount() :
prev_time_(-1),
prev_value_('\0'),
high_time_(0),
transition_count_(0)
{
VcdReader reader(sta);
return reader.read(filename);
}
Vcd
VcdReader::read(const char *filename)
void
VcdCount::addPin(const Pin *pin)
{
Vcd vcd(this);
vcd_ = &vcd;
stream_ = gzopen(filename, "r");
if (stream_) {
Stats stats(debug_, report_);
filename_ = filename;
file_line_ = 1;
stmt_line_ = 1;
string token = getToken();
while (!token.empty()) {
if (token == "$date")
vcd_->setDate(readStmtString());
else if (token == "$comment")
vcd_->setComment(readStmtString());
else if (token == "$version")
vcd_->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");
pins_.push_back(pin);
}
void
VcdCount::incrCounts(VcdTime time,
char value)
{
// Initial value does not coontribute to transitions or high time.
if (prev_time_ != -1) {
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
VcdCount::highTime(VcdTime time_max) const
{
if (prev_value_ == '1')
return high_time_ + time_max - prev_time_;
else
throw FileNotReadable(filename);
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;
return high_time_;
}
////////////////////////////////////////////////////////////////
static void
reportWaveforms(Vcd &vcd,
Report *report);
void
reportVcdWaveforms(const char *filename,
StaState *sta)
// VcdCount[bit]
typedef vector<VcdCount> VcdCounts;
// ID -> VcdCount[bit]
typedef map<string, VcdCounts> VcdIdCountsMap;
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
reportWaveforms(Vcd &vcd,
Report *report)
void
VcdCountReader::setTimeUnit(const string &,
double time_unit_scale,
double time_scale)
{
report->reportLine("Date: %s", vcd.date().c_str());
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();
time_scale_ = time_scale * time_unit_scale;
}
int max_var_name_length = vcd.maxVarNameLength();
for (VcdVar *var : vcd.vars()) {
string line;
stringPrint(line, " %-*s",
static_cast<int>(max_var_name_length),
var->name().c_str());
const VcdValues &var_values = vcd.values(var);
if (!var_values.empty()) {
size_t value_index = 0;
VcdValue var_value = var_values[value_index];
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) {
if (time >= next_value_time) {
if (value_index < var_values.size() - 1)
value_index++;
var_value = var_values[value_index];
if (value_index < var_values.size())
next_value_time = var_values[value_index + 1].time();
}
if (var_value.value()) {
// 01UZX
char value = var_value.value();
char prev_value = prev_var_value.value();
if (var->width() == 1) {
if (value == '0' || value == '1') {
for (int z = 0; z < zoom; z++) {
if (z == 0
&& value != prev_value
&& (prev_value == '0'
|| prev_value == '1'))
line += (prev_value == '1') ? "" : "";
else
line += (value == '1') ? "" : "";
}
}
else {
string field;
stringPrint(field, "%-*c", zoom, value);
line += field;
void
VcdCountReader::setTimeMax(VcdTime time_max)
{
time_max_ = time_max;
}
bool
VcdCountReader::varIdValid(const string &)
{
return true;
}
void
VcdCountReader::makeVar(const VcdScope &scope,
const string &name,
VcdVarType type,
size_t width,
const string &id)
{
if (type == VcdVarType::wire
|| type == VcdVarType::reg) {
string path_name;
bool first = true;
for (const string &context : scope) {
if (!first)
path_name += '/';
path_name += context;
first = false;
}
size_t scope_length = strlen(scope_);
// string::starts_with in c++20
if (scope_length == 0
|| path_name.substr(0, scope_length) == scope_) {
path_name += '/';
path_name += name;
// Strip the scope from the name.
string var_scoped = path_name.substr(scope_length + 1);
if (width == 1) {
string pin_name = netVerilogToSta(var_scoped.c_str());
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 {
string field;
stringPrint(field, "%-*c", zoom, value);
line += field;
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 {
// bus
string field;
stringPrint(field, "%-*" PRIX64, zoom, var_value.busValue());
line += field;
}
prev_var_value = var_value;
else
report_->warn(1451, "problem parsing bus %s.", var_scoped.c_str());
}
}
report->reportLineString(line);
}
}
void
reportVcdVarValues(const char *filename,
const char *var_name,
StaState *sta)
VcdCountReader::addVarPin(const string &pin_name,
const string &id,
size_t width,
size_t bit_idx)
{
Vcd vcd = readVcdFile(filename, sta);
VcdVar *var = vcd.var(var_name);
if (var) {
Report *report = sta->report();
for (const VcdValue &var_value : vcd.values(var)) {
double time = var_value.time() * vcd.timeUnitScale();
char value = var_value.value();
if (value == '\0')
report->reportLine("%.2e %" PRIu64,
time, var_value.busValue());
else
report->reportLine("%.2e %c", time, value);
const Pin *pin = sdc_network_->findPin(pin_name.c_str());
if (pin
&& !sdc_network_->isHierarchical(pin)
&& !sdc_network_->direction(pin)->isInternal()) {
VcdCounts &vcd_counts = vcd_count_map_[id];
vcd_counts.resize(width);
vcd_counts[bit_idx].addPin(pin);
debugPrint(debug_, "read_vcd_activities", 2, "id %s pin %s",
id.c_str(),
pin_name.c_str());
}
}
void
VcdCountReader::varAppendValue(const string &id,
VcdTime time,
char value)
{
auto itr = vcd_count_map_.find(id);
if (itr != vcd_count_map_.end()) {
VcdCounts &vcd_counts = itr->second;
if (debug_->check("read_vcd_activities", 3)) {
for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) {
VcdCount &vcd_count = vcd_counts[bit_idx];
for (const Pin *pin : vcd_count.pins()) {
debugPrint(debug_, "read_vcd_activities", 3, "%s time %" PRIu64 " value %c",
sdc_network_->pathName(pin),
time,
value);
}
}
}
for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) {
VcdCount &vcd_count = vcd_counts[bit_idx];
vcd_count.incrCounts(time, value);
}
}
}
void
VcdCountReader::varAppendBusValue(const string &id,
VcdTime time,
int64_t bus_value)
{
auto itr = vcd_count_map_.find(id);
if (itr != vcd_count_map_.end()) {
VcdCounts &vcd_counts = itr->second;
for (size_t bit_idx = 0; bit_idx < vcd_counts.size(); bit_idx++) {
char bit_value = ((bus_value >> bit_idx) & 0x1) ? '1' : '0';
VcdCount &vcd_count = vcd_counts[bit_idx];
if (debug_->check("read_vcd_activities", 3)) {
for (const Pin *pin : vcd_count.pins()) {
debugPrint(debug_, "read_vcd_activities", 3, "%s time %" PRIu64 " value %c",
sdc_network_->pathName(pin),
time,
bit_value);
}
}
vcd_count.incrCounts(time, bit_value);
}
}
}
////////////////////////////////////////////////////////////////
class ReadVcdActivities : public StaState
{
public:
ReadVcdActivities(const char *filename,
const char *scope,
Sta *sta);
void readActivities();
private:
void setActivities();
void checkClkPeriod(const Pin *pin,
double transition_count);
const char *filename_;
VcdCountReader vcd_reader_;
VcdParse vcd_parse_;
Power *power_;
std::set<const Pin*> annotated_pins_;
static constexpr double sim_clk_period_tolerance_ = .1;
};
void
readVcdActivities(const char *filename,
const char *scope,
Sta *sta)
{
ReadVcdActivities reader(filename, scope, sta);
reader.readActivities();
}
ReadVcdActivities::ReadVcdActivities(const char *filename,
const char *scope,
Sta *sta) :
StaState(sta),
filename_(filename),
vcd_reader_(scope, sdc_network_, report_, debug_),
vcd_parse_(report_, debug_),
power_(sta->power())
{
}
void
ReadVcdActivities::readActivities()
{
ClockSeq *clks = sdc_->clocks();
if (clks->empty())
report_->error(805, "No clocks have been defined.");
vcd_parse_.read(filename_, &vcd_reader_);
if (vcd_reader_.timeMax() > 0)
setActivities();
else
report_->warn(1450, "VCD max time is zero.");
report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size());
}
void
ReadVcdActivities::setActivities()
{
VcdTime time_max = vcd_reader_.timeMax();
double time_scale = vcd_reader_.timeScale();
for (auto& [id, vcd_counts] : vcd_reader_.countMap()) {
for (const VcdCount &vcd_count : vcd_counts) {
double transition_count = vcd_count.transitionCount();
VcdTime high_time = vcd_count.highTime(time_max);
float duty = static_cast<double>(high_time) / time_max;
float density = transition_count / (time_max * time_scale);
if (debug_->check("read_vcd_activities", 1)) {
for (const Pin *pin : vcd_count.pins()) {
debugPrint(debug_, "read_vcd_activities", 1,
"%s transitions %.1f activity %.2f duty %.2f",
sdc_network_->pathName(pin),
transition_count,
density,
duty);
}
}
for (const Pin *pin : vcd_count.pins()) {
power_->setUserActivity(pin, density, duty, PwrActivityOrigin::vcd);
if (sdc_->isLeafPinClock(pin))
checkClkPeriod(pin, transition_count);
annotated_pins_.insert(pin);
}
}
}
}
void
ReadVcdActivities::checkClkPeriod(const Pin *pin,
double transition_count)
{
VcdTime time_max = vcd_reader_.timeMax();
double time_scale = vcd_reader_.timeScale();
double sim_period = time_max * time_scale / (transition_count / 2.0);
ClockSet *clks = sdc_->findLeafPinClocks(pin);
if (clks) {
for (Clock *clk : *clks) {
double clk_period = clk->period();
if (abs((clk_period - sim_period) / clk_period) > sim_clk_period_tolerance_)
// Warn if sim clock period differs from SDC by more than 10%.
report_->warn(1452, "clock %s vcd period %s differs from SDC clock period %s",
clk->name(),
delayAsString(sim_period, this),
delayAsString(clk_period, this));
}
}
}

View File

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

View File

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

View File

@ -56,7 +56,7 @@ proc_redirect read_sdf {
################################################################
define_cmd_args "report_annotated_delay" \
{[-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]}
proc_redirect report_annotated_delay {
@ -92,7 +92,7 @@ proc_redirect report_annotated_delay {
define_cmd_args "report_annotated_check" \
{[-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]}
proc_redirect report_annotated_check {

View File

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

View File

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

View File

@ -93,14 +93,13 @@ PathGroup::~PathGroup()
}
bool
PathGroup::savable(PathEnd *path_end)
PathGroup::saveable(PathEnd *path_end)
{
float threshold;
{
LockGuard lock(lock_);
threshold = threshold_;
}
bool savable = false;
if (compare_slack_) {
// Crpr increases the slack, so check the slack
// without crpr first because it is expensive to find.
@ -110,17 +109,49 @@ PathGroup::savable(PathEnd *path_end)
&& delayLessEqual(slack, slack_max_, sta_)) {
// Now check with crpr.
slack = path_end->slack(sta_);
savable = delayLessEqual(slack, threshold, sta_)
return delayLessEqual(slack, threshold, sta_)
&& delayLessEqual(slack, slack_max_, sta_)
&& delayGreaterEqual(slack, slack_min_, sta_);
}
}
else {
const Arrival &arrival = path_end->dataArrivalTime(sta_);
savable = !delayIsInitValue(arrival, min_max_)
return !delayIsInitValue(arrival, min_max_)
&& 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
@ -565,7 +596,7 @@ void
MakePathEnds1::visitPathEnd(PathEnd *path_end,
PathGroup *group)
{
if (group->savable(path_end)) {
if (group->saveable(path_end)) {
// Only keep the path end with the smallest slack/latest arrival.
PathEnd *worst_end = ends_.findKey(group);
if (worst_end) {
@ -588,7 +619,7 @@ MakePathEnds1::vertexEnd(Vertex *)
PathGroup *group;
PathEnd *end;
group_iter.next(group, end);
// visitPathEnd already confirmed slack is savable.
// visitPathEnd already confirmed slack is saveable.
if (end) {
group->insert(end);
// Clear ends_ for next vertex.
@ -701,7 +732,8 @@ MakePathEndsAll::vertexEnd(Vertex *)
path_end->path()->tag(sta_)->index());
// Give the group a copy of the path end because
// 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());
unique_ends.insert(path_end);
n++;
@ -788,7 +820,8 @@ PathGroups::enumPathEnds(PathGroup *group,
PathGroupIterator *end_iter = group->iterator();
while (end_iter->hasNext()) {
PathEnd *end = end_iter->next();
if (group->savable(end))
if (group->saveable(end)
|| group->enumMinSlackUnderMin(end))
path_enum.insert(end);
}
delete end_iter;
@ -797,7 +830,10 @@ PathGroups::enumPathEnds(PathGroup *group,
// Parallel path enumeratation to find the endpoint_path_count/max path ends.
for (int n = 0; path_enum.hasNext() && n < group_path_count; n++) {
PathEnd *end = path_enum.next();
group->insert(end);
if (group->saveable(end))
group->insert(end);
else
delete end;
}
}

View File

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

View File

@ -55,6 +55,15 @@
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
hierPinsThruEdge(const Edge *edge,
const Network *network,
@ -326,14 +335,18 @@ void
ReportPath::reportPathEnds(PathEndSeq *ends)
{
reportPathEndHeader();
PathEndSeq::Iterator end_iter(ends);
PathEnd *prev_end = nullptr;
while (end_iter.hasNext()) {
PathEnd *end = end_iter.next();
reportEndpointHeader(end, prev_end);
end->reportFull(this);
reportBlankLine();
prev_end = end;
if (ends && !ends->empty()) {
PathEndSeq::Iterator end_iter(ends);
PathEnd *prev_end = nullptr;
while (end_iter.hasNext()) {
PathEnd *end = end_iter.next();
reportPathEnd(end, prev_end, !end_iter.hasNext());
prev_end = end;
}
}
else {
if (format_ != ReportPathFormat::json)
report_->reportLine("No paths found.");
}
reportPathEndFooter();
}
@ -1086,9 +1099,9 @@ ReportPath::reportJson(const PathEnd *end,
const Pin *startpoint = expanded.startPath()->vertex(this)->pin();
const Pin *endpoint = expanded.endPath()->vertex(this)->pin();
stringAppend(result, " \"startpoint\": \"%s\",\n",
network_->pathName(startpoint));
sdc_network_->pathName(startpoint));
stringAppend(result, " \"endpoint\": \"%s\",\n",
network_->pathName(endpoint));
sdc_network_->pathName(endpoint));
const ClockEdge *src_clk_edge = end->sourceClkEdge(this);
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++) {
const PathRef *path = expanded.path(i);
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, "");
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",
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;
bool exists;
network_->location(pin, x, y, exists);
@ -1186,6 +1240,10 @@ ReportPath::reportJson(const PathExpanded &expanded,
stringAppend(result, "%*s \"arrival\": %.3e,\n",
indent, "",
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",
indent, "",
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
hierPinsThruEdge(const Edge *edge,
const Network *network,
@ -3557,6 +3606,7 @@ hierPinsAbove(const Net *net,
if (hpin_net)
hierPinsAbove(hpin_net, network, pins_above);
}
delete term_iter;
}
}

View File

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

View File

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

View File

@ -420,14 +420,9 @@ define_cmd_args "report_checks" \
proc_redirect report_checks {
global sta_report_unconstrained_paths
parse_report_path_options "report_checks" args "full" 0
set path_ends [find_timing_paths_cmd "report_checks" args]
if { $path_ends == {} } {
report_line "No paths found."
} else {
report_path_ends $path_ends
}
report_path_ends $path_ends
}
################################################################
@ -1091,19 +1086,6 @@ proc parse_path_group_arg { group_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" \

View File

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

View File

@ -20,6 +20,13 @@
fprintf(stderr, "Error: out of memory.\n");
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) {
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "Error: ", excp.what(), nullptr);

View File

@ -39,22 +39,26 @@
#include "PathEnd.hh"
#include "SearchClass.hh"
#include "CircuitSim.hh"
#include "ArcDelayCalc.hh"
#include "Property.hh"
#include "Sta.hh"
#include "TclTypeHelpers.hh"
namespace sta {
typedef MinPulseWidthCheckSeq::Iterator MinPulseWidthCheckSeqIterator;
typedef MinMaxAll MinMaxAllNull;
#if TCL_MAJOR_VERSION < 9
typedef int Tcl_Size;
#endif
template <class TYPE>
Vector<TYPE> *
tclListSeqPtr(Tcl_Obj *const source,
swig_type_info *swig_type,
Tcl_Interp *interp)
{
int argc;
Tcl_Size argc;
Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
@ -78,7 +82,7 @@ tclListSeq(Tcl_Obj *const source,
swig_type_info *swig_type,
Tcl_Interp *interp)
{
int argc;
Tcl_Size argc;
Tcl_Obj **argv;
std::vector<TYPE> seq;
@ -100,7 +104,7 @@ tclListSetPtr(Tcl_Obj *const source,
swig_type_info *swig_type,
Tcl_Interp *interp)
{
int argc;
Tcl_Size argc;
Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
&& argc > 0) {
@ -123,7 +127,7 @@ tclListSet(Tcl_Obj *const source,
swig_type_info *swig_type,
Tcl_Interp *interp)
{
int argc;
Tcl_Size argc;
Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
&& argc > 0) {
@ -147,7 +151,7 @@ tclListNetworkSet(Tcl_Obj *const source,
Tcl_Interp *interp,
const Network *network)
{
int argc;
Tcl_Size argc;
Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
&& argc > 0) {
@ -171,7 +175,7 @@ tclListNetworkSet1(Tcl_Obj *const source,
Tcl_Interp *interp,
const Network *network)
{
int argc;
Tcl_Size argc;
Tcl_Obj **argv;
SET_TYPE set(network);
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK
@ -186,68 +190,6 @@ tclListNetworkSet1(Tcl_Obj *const source,
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.
@ -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
using namespace sta;
@ -913,7 +738,7 @@ using namespace sta;
}
%typemap(in) FloatSeq* {
int argc;
Tcl_Size argc;
Tcl_Obj **argv;
FloatSeq *floats = nullptr;
@ -958,7 +783,7 @@ using namespace sta;
}
%typemap(in) IntSeq* {
int argc;
Tcl_Size argc;
Tcl_Obj **argv;
IntSeq *ints = nullptr;
@ -1286,6 +1111,10 @@ using namespace sta;
Tcl_SetObjResult(interp, obj);
}
%typemap(in) PathEndSeq* {
$1 = tclListSeqPtr<PathEnd*>($input, SWIGTYPE_p_PathEnd, interp);
}
%typemap(out) PathEndSeq* {
Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
const PathEndSeq *path_ends = $1;
@ -1541,7 +1370,7 @@ using namespace sta;
Tcl_Obj *obj;
const char *str;
str = stringPrintTmp("%.5e", activity.activity());
str = stringPrintTmp("%.5e", activity.density());
obj = Tcl_NewStringObj(str, strlen(str));
Tcl_ListObjAppendElement(interp, list, obj);
@ -1586,7 +1415,7 @@ using namespace sta;
%typemap(in) ArcDcalcArgSeq {
Tcl_Obj *const source = $input;
int argc;
Tcl_Size argc;
Tcl_Obj **argv;
Sta *sta = Sta::sta();

201
tcl/TclTypeHelpers.cc Normal file
View File

@ -0,0 +1,201 @@
// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, Parallax Software, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "TclTypeHelpers.hh"
#include "Liberty.hh"
#include "Network.hh"
#include "Sta.hh"
namespace sta {
StringSet *
tclListSetConstChar(Tcl_Obj *const source,
Tcl_Interp *interp)
{
Tcl_Size argc;
Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) {
StringSet *set = new StringSet;
for (int i = 0; i < argc; i++) {
int length;
const char *str = Tcl_GetStringFromObj(argv[i], &length);
set->insert(str);
}
return set;
}
else
return nullptr;
}
StringSeq *
tclListSeqConstChar(Tcl_Obj *const source,
Tcl_Interp *interp)
{
Tcl_Size argc;
Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) {
StringSeq *seq = new StringSeq;
for (int i = 0; i < argc; i++) {
int length;
const char *str = Tcl_GetStringFromObj(argv[i], &length);
seq->push_back(str);
}
return seq;
}
else
return nullptr;
}
StdStringSet *
tclListSetStdString(Tcl_Obj *const source,
Tcl_Interp *interp)
{
Tcl_Size argc;
Tcl_Obj **argv;
if (Tcl_ListObjGetElements(interp, source, &argc, &argv) == TCL_OK) {
StdStringSet *set = new StdStringSet;
for (int i = 0; i < argc; i++) {
int length;
const char *str = Tcl_GetStringFromObj(argv[i], &length);
set->insert(str);
}
return set;
}
else
return nullptr;
}
void
tclArgError(Tcl_Interp *interp,
const char *msg,
const char *arg)
{
// Swig does not add try/catch around arg parsing so this cannot use Report::error.
string error_msg = "Error: ";
error_msg += msg;
char *error = stringPrint(error_msg.c_str(), arg);
Tcl_SetResult(interp, error, TCL_VOLATILE);
stringDelete(error);
}
void
objectListNext(const char *list,
const char *type,
// Return values.
bool &type_match,
const char *&next)
{
// Default return values (failure).
type_match = false;
next = nullptr;
// _hexaddress_p_type
const char *s = list;
char ch = *s++;
if (ch == '_') {
while (*s && isxdigit(*s))
s++;
if ((s - list - 1) == sizeof(void*) * 2
&& *s && *s++ == '_'
&& *s && *s++ == 'p'
&& *s && *s++ == '_') {
const char *t = type;
while (*s && *s != ' ') {
if (*s != *t)
return;
s++;
t++;
}
type_match = true;
if (*s)
next = s + 1;
else
next = nullptr;
}
}
}
Tcl_Obj *
tclArcDcalcArg(ArcDcalcArg &gate,
Tcl_Interp *interp)
{
Sta *sta = Sta::sta();
const Network *network = sta->network();
const Instance *drvr = network->instance(gate.drvrPin());
const TimingArc *arc = gate.arc();
Tcl_Obj *list = Tcl_NewListObj(0, nullptr);
Tcl_Obj *obj;
const char *inst_name = network->pathName(drvr);
obj = Tcl_NewStringObj(inst_name, strlen(inst_name));
Tcl_ListObjAppendElement(interp, list, obj);
const char *from_name = arc->from()->name();
obj = Tcl_NewStringObj(from_name, strlen(from_name));
Tcl_ListObjAppendElement(interp, list, obj);
const char *from_edge = arc->fromEdge()->asString();
obj = Tcl_NewStringObj(from_edge, strlen(from_edge));
Tcl_ListObjAppendElement(interp, list, obj);
const char *to_name = arc->to()->name();
obj = Tcl_NewStringObj(to_name, strlen(to_name));
Tcl_ListObjAppendElement(interp, list, obj);
const char *to_edge = arc->toEdge()->asString();
obj = Tcl_NewStringObj(to_edge, strlen(to_edge));
Tcl_ListObjAppendElement(interp, list, obj);
const char *input_delay = delayAsString(gate.inputDelay(), sta, 3);
obj = Tcl_NewStringObj(input_delay, strlen(input_delay));
Tcl_ListObjAppendElement(interp, list, obj);
return list;
}
ArcDcalcArg
arcDcalcArgTcl(Tcl_Obj *obj,
Tcl_Interp *interp)
{
Sta *sta = Sta::sta();
sta->ensureGraph();
int list_argc;
Tcl_Obj **list_argv;
if (Tcl_ListObjGetElements(interp, obj, &list_argc, &list_argv) == TCL_OK) {
const char *input_delay = "0.0";
int length;
if (list_argc == 6)
input_delay = Tcl_GetStringFromObj(list_argv[5], &length);
if (list_argc == 5 || list_argc == 6) {
return makeArcDcalcArg(Tcl_GetStringFromObj(list_argv[0], &length),
Tcl_GetStringFromObj(list_argv[1], &length),
Tcl_GetStringFromObj(list_argv[2], &length),
Tcl_GetStringFromObj(list_argv[3], &length),
Tcl_GetStringFromObj(list_argv[4], &length),
input_delay, sta);
}
else
sta->report()->warn(2140, "Delay calc arg requires 5 or 6 args.");
}
return ArcDcalcArg();
}
} // namespace

View File

@ -192,10 +192,13 @@ proc sta_warn { msg_id msg } {
proc sta_error { msg_id msg } {
variable sdc_file
variable sdc_line
if { [info exists sdc_file] } {
error "Error: [file tail $sdc_file] line $sdc_line, $msg"
} else {
error "Error: $msg"
if { ! [is_suppressed $msg_id] } {
if { [info exists sdc_file] } {
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
define_cmd_args "elapsed_run_time" {}
define_cmd_args "user_run_time" {}

BIN
test/asap7_ccsn.lib.gz Normal file

Binary file not shown.

0
test/liberty_ccsn.ok Normal file
View File

2
test/liberty_ccsn.tcl Normal file
View File

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

View File

@ -2,11 +2,11 @@ Warning: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not foun
Group Internal Switching Leakage Total
Power Power Power Power (Watts)
----------------------------------------------------------------
Sequential 3.07e-04 4.75e-05 2.96e-10 3.54e-04 40.1%
Combinational 1.58e-04 2.04e-04 6.86e-10 3.62e-04 41.0%
Sequential 3.07e-04 4.76e-05 2.96e-10 3.54e-04 40.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%
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%
----------------------------------------------------------------
Total 5.11e-04 3.72e-04 1.00e-09 8.84e-04 100.0%
57.9% 42.1% 0.0%
Total 5.12e-04 3.73e-04 1.00e-09 8.85e-04 100.0%
57.8% 42.2% 0.0%

View File

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

View File

@ -75,6 +75,9 @@ proc parse_args {} {
lappend app_options $threads
set argv [lrange $argv 2 end]
} elseif { $arg == "-valgrind" } {
if { ![find_valgrind] } {
error "valgrind not found."
}
set use_valgrind 1
set argv [lrange $argv 1 end]
} 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 } {
global test_groups errors

View File

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

View File

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

78
test/report_json1.ok Normal file
View File

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

7
test/report_json1.tcl Normal file
View File

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

3
test/report_json2.ok Normal file
View File

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

6
test/report_json2.tcl Normal file
View File

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

12
test/suppress_msg.ok Normal file
View File

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

56
test/suppress_msg.tcl Normal file
View File

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

View File

@ -11,11 +11,19 @@ module counter(clk, reset, in, out);
(* src = "synthesis/tests/counter.v:18.14-18.19" *)
input reset;
input in;
wire mid;
(* bottom_bound = 1'sh0 *)
(* src = "synthesis/tests/counter.v:22.3-28.6", attr1 = "test_attr1", attr2 = "test_attr2" *)
sky130_fd_sc_hd__dfrtp_1 _1415_ (
.CLK(clk),
.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),
.RESET_B(reset)
);

View File

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

View File

@ -169,59 +169,71 @@ Report::printBufferLine()
////////////////////////////////////////////////////////////////
void
Report::warn(int /* id */,
Report::warn(int id,
const char *fmt,
...)
{
va_list args;
va_start(args, fmt);
printToBuffer("Warning: ");
printToBufferAppend(fmt, args);
printBufferLine();
va_end(args);
// Skip suppressed messages.
if (!isSuppressed(id)) {
va_list args;
va_start(args, fmt);
printToBuffer("Warning: ");
printToBufferAppend(fmt, args);
printBufferLine();
va_end(args);
}
}
void
Report::vwarn(int /* id */,
Report::vwarn(int id,
const char *fmt,
va_list args)
{
printToBuffer("Warning: ");
printToBufferAppend(fmt, args);
printBufferLine();
// Skip suppressed messages.
if (!isSuppressed(id)) {
printToBuffer("Warning: ");
printToBufferAppend(fmt, args);
printBufferLine();
}
}
void
Report::fileWarn(int /* id */,
Report::fileWarn(int id,
const char *filename,
int line,
const char *fmt,
...)
{
va_list args;
va_start(args, fmt);
printToBuffer("Warning: %s line %d, ", filename, line);
printToBufferAppend(fmt, args);
printBufferLine();
va_end(args);
// Skip suppressed messages.
if (!isSuppressed(id)) {
va_list args;
va_start(args, fmt);
printToBuffer("Warning: %s line %d, ", filename, line);
printToBufferAppend(fmt, args);
printBufferLine();
va_end(args);
}
}
void
Report::vfileWarn(int /* id */,
Report::vfileWarn(int id,
const char *filename,
int line,
const char *fmt,
va_list args)
{
printToBuffer("Warning: %s line %d, ", filename, line);
printToBufferAppend(fmt, args);
printBufferLine();
// Skip suppressed messages.
if (!isSuppressed(id)) {
printToBuffer("Warning: %s line %d, ", filename, line);
printToBufferAppend(fmt, args);
printBufferLine();
}
}
////////////////////////////////////////////////////////////////
void
Report::error(int /* id */,
Report::error(int id,
const char *fmt, ...)
{
va_list args;
@ -229,21 +241,21 @@ Report::error(int /* id */,
// No prefix msg, no \n.
printToBuffer(fmt, args);
va_end(args);
throw ExceptionMsg(buffer_);
throw ExceptionMsg(buffer_, isSuppressed(id));
}
void
Report::verror(int /* id */,
Report::verror(int id,
const char *fmt,
va_list args)
{
// No prefix msg, no \n.
printToBuffer(fmt, args);
throw ExceptionMsg(buffer_);
throw ExceptionMsg(buffer_, isSuppressed(id));
}
void
Report::fileError(int /* id */,
Report::fileError(int id,
const char *filename,
int line,
const char *fmt,
@ -255,11 +267,11 @@ Report::fileError(int /* id */,
printToBuffer("%s line %d, ", filename, line);
printToBufferAppend(fmt, args);
va_end(args);
throw ExceptionMsg(buffer_);
throw ExceptionMsg(buffer_, isSuppressed(id));
}
void
Report::vfileError(int /* id */,
Report::vfileError(int id,
const char *filename,
int line,
const char *fmt,
@ -268,7 +280,7 @@ Report::vfileError(int /* id */,
// No prefix msg, no \n.
printToBuffer("%s line %d, ", filename, line);
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
Report::logBegin(const char *filename)
{

View File

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

View File

@ -148,6 +148,24 @@ report_line(const char *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
fflush()
{

View File

@ -1822,7 +1822,7 @@ VerilogReader::linkNetwork(const char *top_cell_name,
VerilogModule *module = this->module(top_cell);
if (module) {
// 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_);
VerilogNetSeq::Iterator port_iter(module->ports());
while (port_iter.hasNext()) {