read_vcd, report_activity_annotation resolves #162 resolves $158

commit 02be8357f8ba158292faab0e8d8b8d220e69c842
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Jan 15 15:59:20 2025 -0700

    sort regressions

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit b0e1d863d34853b70d342c6bb0baa400a08db207
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Jan 15 15:44:07 2025 -0700

    PowerResult::incf*

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 23ed0ed05d8bbf83cc5b6fa7598f1253cbe2b222
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sun Jan 12 17:49:24 2025 -0700

    gzip vcd/saif

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit a5a1a7b80e00b2deb51505b2e60312e3c29284fe
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sun Jan 12 17:12:09 2025 -0700

    report_activity_annotation sort pins

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 6c149cc6d66addda4eee5f56dbe4731876f1efa9
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sun Jan 12 16:47:02 2025 -0700

    vcd/saif do not annotate internal pins

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit fe8c7c7fc7b2878cb16c7d2a57e1161555f9a36f
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sat Jan 11 17:09:22 2025 -0700

    report_activity_annotation

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 8a8f87ba05c6c6f31017288d573c23e92cac4f9e
Author: James Cherry <cherry@parallaxsw.com>
Date:   Fri Jan 10 11:57:22 2025 -0700

    ReadVcdActivies factor VcdCountReader

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit aa70ae987c4a749e53ae2f93c8ff04a3c28b0b08
Author: James Cherry <cherry@parallaxsw.com>
Date:   Fri Jan 10 09:06:09 2025 -0700

    vcd high time

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 242493e992949ee72493f4f15a0fe92630ad2f03
Author: James Cherry <cherry@parallaxsw.com>
Date:   Thu Jan 9 17:14:02 2025 -0700

    comment

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 8621a83d7abf5efcf54c24a1a16bba0c8eb5e441
Author: James Cherry <cherry@parallaxsw.com>
Date:   Thu Jan 9 17:11:22 2025 -0700

    ReadVcdActivities rm sta_

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit fc0327c39c5aef6d09580795f6a13abf9b9f8266
Author: James Cherry <cherry@parallaxsw.com>
Date:   Thu Jan 9 09:36:30 2025 -0700

    var name

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit a5b6e2ac66cef9355a9a87edcc185b483f5781ce
Author: James Cherry <cherry@parallaxsw.com>
Date:   Thu Jan 9 08:23:50 2025 -0700

    vcd passes

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit c0f6d268542b3dd431882cfa4e3d131d6d2fb933
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Jan 8 20:19:27 2025 -0700

    vcd

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 8181e206ee39e4c3fdd69ef1d3463092763c3d2b
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Jan 8 19:41:59 2025 -0700

    vcd debug

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 652736654b853d1f7fb64545c6a48500e1e3b83f
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Jan 8 19:17:06 2025 -0700

    vcd bus support

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 03c877512d92b0148fd62cbc636da58ef6fb25ff
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Jan 8 19:11:21 2025 -0700

    vcd

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 53bbd97a28d619ca87a546f34a3643c227b9df78
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Jan 8 16:02:47 2025 -0700

    rm Vcd structs, ReadVcdActivities -> VcdReader

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 745efff41221c3b06ba19931e4f8a58e5b0758ae
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Jan 8 15:46:49 2025 -0700

    vcd skip hpins

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit a9a6177e8b671f0619809611984ed16f2d1a5b7a
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Jan 8 15:39:41 2025 -0700

    ReadVcdActivies rm vcd_

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 8b51b13b02c257d10385312d246e0da1f11b72f2
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Jan 8 15:28:31 2025 -0700

    power_vcd1 passes

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 744669797d1a4189c417d9d2f359cbd1f5bc7113
Author: James Cherry <cherry@parallaxsw.com>
Date:   Wed Jan 8 10:16:35 2025 -0700

    vcd

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 0f0aeac1cd8349302b758ce4b79fd753063565f2
Author: James Cherry <cherry@parallaxsw.com>
Date:   Mon Jan 6 16:20:13 2025 -0800

    vcd reader

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 01d296ce9b3ad8ac7e6dddf28a41a718b4138869
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sun Jan 5 16:08:41 2025 -0800

    vcd rm max_var_width

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 9ffdb19074e5c38464b88ead0ec751ae7d3c506c
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sun Jan 5 16:03:06 2025 -0800

    vcd rm max_var_name_length_

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 5c577143d4a166bd133a9d97d1761b52b8e122dd
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sun Jan 5 15:58:29 2025 -0800

    ReadVcdActivities isa VcdReader

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 6914405d0799b967a92634fe1aa251aaea4cdfbc
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sat Jan 4 17:24:57 2025 -0800

    vcd parse rm StaState dependence

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit a0bd7aa5eee2e87c883776bf4840d117335e4a8b
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sat Jan 4 16:41:55 2025 -0800

    vcd headers

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

commit 4842d19d7a5c47d6ee46f297cb9404ab77fe2662
Author: James Cherry <cherry@parallaxsw.com>
Date:   Sat Jan 4 15:59:46 2025 -0800

    refactor vcd parser

    Signed-off-by: James Cherry <cherry@parallaxsw.com>

Signed-off-by: James Cherry <cherry@parallaxsw.com>
This commit is contained in:
James Cherry 2025-01-15 16:20:21 -07:00
parent 3797548fd8
commit e7eebc98a3
23 changed files with 1041 additions and 24589 deletions

View File

@ -199,10 +199,9 @@ set(STA_SOURCE
spice/Xyce.cc
power/Power.cc
power/ReadVcdActivities.cc
power/SaifReader.cc
power/Vcd.cc
power/VcdReader.cc
power/SaifReader.cc
power/VcdParse.cc
util/Debug.cc
util/DispatchQueue.cc

View File

@ -62,6 +62,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
-------------------------

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

@ -45,7 +45,7 @@ public:
void setActivity(float activity);
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,
@ -69,11 +69,14 @@ 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_;

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

@ -865,7 +865,7 @@ Power::findInputInternalPower(const Pin *pin,
related_pg_pin ? related_pg_pin : "no pg_pin");
internal += port_internal;
}
result.internal() += internal;
result.incrInternal(internal);
}
}
}
@ -1024,7 +1024,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
@ -1117,7 +1117,7 @@ Power::findSwitchingPower(const Instance *inst,
activity.activity(),
volt,
switching);
result.switching() += switching;
result.incrSwitching(switching);
}
}
}
@ -1185,7 +1185,7 @@ Power::findLeakagePower(const Instance *inst,
debugPrint(debug_, "power", 2, "leakage %s %.3e",
cell->name(),
leakage);
result.leakage() += leakage;
result.incrLeakage(leakage);
}
PwrActivity
@ -1268,10 +1268,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 PwrActivity(0.0, 0.0, PwrActivityOrigin::unknown);
return PwrActivity();
}
float
@ -1330,6 +1329,117 @@ 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;
}
////////////////////////////////////////////////////////////////
PowerResult::PowerResult() :
internal_(0.0),
switching_(0.0),
@ -1351,6 +1461,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)
{

View File

@ -91,6 +91,8 @@ public:
PwrActivityOrigin origin);
// Activity is toggles per second.
PwrActivity findClkedActivity(const Pin *pin);
void reportActivityAnnotation(bool report_unannotated,
bool report_annotated);
protected:
bool inClockNetwork(const Instance *inst);
@ -199,6 +201,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

@ -20,7 +20,6 @@
#include "Sta.hh"
#include "power/Power.hh"
#include "power/VcdReader.hh"
#include "power/ReadVcdActivities.hh"
#include "power/SaifReader.hh"
using namespace sta;
@ -113,22 +112,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 +123,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

@ -323,6 +323,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"
@ -178,7 +179,9 @@ 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)];

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,433 @@
// 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)
{
VcdReader reader(sta);
return reader.read(filename);
}
Vcd
VcdReader::read(const char *filename)
{
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");
}
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)
VcdCount::VcdCount() :
prev_time_(-1),
prev_value_('\0'),
high_time_(0),
transition_count_(0)
{
}
void
VcdReader::parseTimescale()
VcdCount::addPin(const Pin *pin)
{
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.");
pins_.push_back(pin);
}
void
VcdReader::setTimeUnit(const string &time_unit)
VcdCount::incrCounts(VcdTime time,
char value)
{
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;
// 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
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);
// 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)
{
}
void
reportVcdWaveforms(const char *filename,
StaState *sta)
VcdCountReader::setTimeUnit(const string &,
double time_unit_scale,
double time_scale)
{
Vcd vcd = readVcdFile(filename, sta);
reportWaveforms(vcd, sta->report());
time_scale_ = time_scale * time_unit_scale;
}
static void
reportWaveforms(Vcd &vcd,
Report *report)
void
VcdCountReader::setTimeMax(VcdTime time_max)
{
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();
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();
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 {
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++;
}
}
}
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') ? "" : "";
report_->warn(1451, "problem parsing bus %s.", var_scoped.c_str());
}
}
else {
string field;
stringPrint(field, "%-*c", zoom, value);
line += field;
}
}
else {
string field;
stringPrint(field, "%-*c", zoom, value);
line += field;
}
}
else {
// bus
string field;
stringPrint(field, "%-*" PRIX64, zoom, var_value.busValue());
line += field;
}
prev_var_value = var_value;
}
}
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());
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_;
double clk_period_;
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_),
clk_period_(0.0),
power_(sta->power())
{
}
void
ReadVcdActivities::readActivities()
{
ClockSeq *clks = sdc_->clocks();
if (clks->empty())
report_->error(805, "No clocks have been defined.");
clk_period_ = INF;
for (Clock *clk : *clks)
clk_period_ = min(static_cast<double>(clk->period()), clk_period_);
vcd_parse_.read(filename_, &vcd_reader_);
if (vcd_reader_.timeMax() > 0)
setActivities();
else
report->reportLine("%.2e %c", time, value);
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 activity = transition_count / (time_max * time_scale / clk_period_);
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,
activity,
duty);
}
}
for (const Pin *pin : vcd_count.pins()) {
power_->setUserActivity(pin, activity, 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

@ -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

@ -112,30 +112,30 @@ 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
liberty_arcs_one2one_1
liberty_arcs_one2one_2
liberty_ccsn
liberty_latch3
prima3
report_checks_src_attr
report_json1
report_json2
liberty_latch3
liberty_ccsn
verilog_attribute
}
define_test_group fast [group_tests all]