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:
parent
3797548fd8
commit
e7eebc98a3
|
|
@ -199,10 +199,9 @@ set(STA_SOURCE
|
||||||
spice/Xyce.cc
|
spice/Xyce.cc
|
||||||
|
|
||||||
power/Power.cc
|
power/Power.cc
|
||||||
power/ReadVcdActivities.cc
|
|
||||||
power/SaifReader.cc
|
|
||||||
power/Vcd.cc
|
|
||||||
power/VcdReader.cc
|
power/VcdReader.cc
|
||||||
|
power/SaifReader.cc
|
||||||
|
power/VcdParse.cc
|
||||||
|
|
||||||
util/Debug.cc
|
util/Debug.cc
|
||||||
util/DispatchQueue.cc
|
util/DispatchQueue.cc
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,11 @@ clocks from the list.
|
||||||
|
|
||||||
all_inputs [-no_clocks]
|
all_inputs [-no_clocks]
|
||||||
|
|
||||||
|
The report_activity_annotation command reports power activity annotaton
|
||||||
|
from vcd, saif or the set_input_activity command.
|
||||||
|
|
||||||
|
report_activity_annotation [-report_unannotated] [-report_annotated]
|
||||||
|
|
||||||
Release 2.4.0 2023/01/19
|
Release 2.4.0 2023/01/19
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
|
@ -2,11 +2,11 @@
|
||||||
read_liberty sky130hd_tt.lib.gz
|
read_liberty sky130hd_tt.lib.gz
|
||||||
read_verilog gcd_sky130hd.v
|
read_verilog gcd_sky130hd.v
|
||||||
link_design gcd
|
link_design gcd
|
||||||
|
|
||||||
read_sdc gcd_sky130hd.sdc
|
read_sdc gcd_sky130hd.sdc
|
||||||
read_spef gcd_sky130hd.spef
|
read_spef gcd_sky130hd.spef
|
||||||
# Generate vcd file
|
# Generate vcd file
|
||||||
# iverilog -o gcd_tb gcd_tb.v
|
# iverilog -o gcd_tb gcd_tb.v
|
||||||
# vvp gcd_tb
|
# vvp gcd_tb
|
||||||
read_vcd -scope gcd_tb/gcd1 gcd_sky130hd.vcd
|
read_vcd -scope gcd_tb/gcd1 gcd_sky130hd.vcd.gz
|
||||||
|
report_activity_annotation
|
||||||
report_power
|
report_power
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ public:
|
||||||
void setActivity(float activity);
|
void setActivity(float activity);
|
||||||
float duty() const { return duty_; }
|
float duty() const { return duty_; }
|
||||||
void setDuty(float duty);
|
void setDuty(float duty);
|
||||||
PwrActivityOrigin origin() { return origin_; }
|
PwrActivityOrigin origin() const { return origin_; }
|
||||||
void setOrigin(PwrActivityOrigin origin);
|
void setOrigin(PwrActivityOrigin origin);
|
||||||
const char *originName() const;
|
const char *originName() const;
|
||||||
void set(float activity,
|
void set(float activity,
|
||||||
|
|
@ -69,11 +69,14 @@ class PowerResult
|
||||||
public:
|
public:
|
||||||
PowerResult();
|
PowerResult();
|
||||||
void clear();
|
void clear();
|
||||||
float &internal() { return internal_; }
|
float internal() const { return internal_; }
|
||||||
float &switching() { return switching_; }
|
float switching() const { return switching_; }
|
||||||
float &leakage() { return leakage_; }
|
float leakage() const { return leakage_; }
|
||||||
float total() const;
|
float total() const;
|
||||||
void incr(PowerResult &result);
|
void incr(PowerResult &result);
|
||||||
|
void incrInternal(float pwr);
|
||||||
|
void incrSwitching(float pwr);
|
||||||
|
void incrLeakage(float pwr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float internal_;
|
float internal_;
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ proc_redirect read_spef {
|
||||||
$coupling_reduction_factor $reduce]
|
$coupling_reduction_factor $reduce]
|
||||||
}
|
}
|
||||||
|
|
||||||
define_cmd_args "report_parasitic_annotation" {-report_unannotated}
|
define_cmd_args "report_parasitic_annotation" {[-report_unannotated]}
|
||||||
|
|
||||||
proc_redirect report_parasitic_annotation {
|
proc_redirect report_parasitic_annotation {
|
||||||
parse_key_args "report_parasitic_annotation" args \
|
parse_key_args "report_parasitic_annotation" args \
|
||||||
|
|
|
||||||
140
power/Power.cc
140
power/Power.cc
|
|
@ -865,7 +865,7 @@ Power::findInputInternalPower(const Pin *pin,
|
||||||
related_pg_pin ? related_pg_pin : "no pg_pin");
|
related_pg_pin ? related_pg_pin : "no pg_pin");
|
||||||
internal += port_internal;
|
internal += port_internal;
|
||||||
}
|
}
|
||||||
result.internal() += internal;
|
result.incrInternal(internal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1024,7 +1024,7 @@ Power::findOutputInternalPower(const LibertyPort *to_port,
|
||||||
related_pg_pin ? related_pg_pin : "no pg_pin");
|
related_pg_pin ? related_pg_pin : "no pg_pin");
|
||||||
internal += port_internal;
|
internal += port_internal;
|
||||||
}
|
}
|
||||||
result.internal() += internal;
|
result.incrInternal(internal);
|
||||||
}
|
}
|
||||||
|
|
||||||
float
|
float
|
||||||
|
|
@ -1117,7 +1117,7 @@ Power::findSwitchingPower(const Instance *inst,
|
||||||
activity.activity(),
|
activity.activity(),
|
||||||
volt,
|
volt,
|
||||||
switching);
|
switching);
|
||||||
result.switching() += switching;
|
result.incrSwitching(switching);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1185,7 +1185,7 @@ Power::findLeakagePower(const Instance *inst,
|
||||||
debugPrint(debug_, "power", 2, "leakage %s %.3e",
|
debugPrint(debug_, "power", 2, "leakage %s %.3e",
|
||||||
cell->name(),
|
cell->name(),
|
||||||
leakage);
|
leakage);
|
||||||
result.leakage() += leakage;
|
result.incrLeakage(leakage);
|
||||||
}
|
}
|
||||||
|
|
||||||
PwrActivity
|
PwrActivity
|
||||||
|
|
@ -1268,10 +1268,9 @@ Power::findSeqActivity(const Instance *inst,
|
||||||
return global_activity_;
|
return global_activity_;
|
||||||
else if (hasSeqActivity(inst, port)) {
|
else if (hasSeqActivity(inst, port)) {
|
||||||
PwrActivity &activity = seqActivity(inst, port);
|
PwrActivity &activity = seqActivity(inst, port);
|
||||||
if (activity.origin() != PwrActivityOrigin::unknown)
|
|
||||||
return activity;
|
return activity;
|
||||||
}
|
}
|
||||||
return PwrActivity(0.0, 0.0, PwrActivityOrigin::unknown);
|
return PwrActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
float
|
float
|
||||||
|
|
@ -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() :
|
PowerResult::PowerResult() :
|
||||||
internal_(0.0),
|
internal_(0.0),
|
||||||
switching_(0.0),
|
switching_(0.0),
|
||||||
|
|
@ -1351,6 +1461,24 @@ PowerResult::total() const
|
||||||
return internal_ + switching_ + leakage_;
|
return internal_ + switching_ + leakage_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PowerResult::incrInternal(float pwr)
|
||||||
|
{
|
||||||
|
internal_ += pwr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PowerResult::incrSwitching(float pwr)
|
||||||
|
{
|
||||||
|
switching_ += pwr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PowerResult::incrLeakage(float pwr)
|
||||||
|
{
|
||||||
|
leakage_ += pwr;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PowerResult::incr(PowerResult &result)
|
PowerResult::incr(PowerResult &result)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,8 @@ public:
|
||||||
PwrActivityOrigin origin);
|
PwrActivityOrigin origin);
|
||||||
// Activity is toggles per second.
|
// Activity is toggles per second.
|
||||||
PwrActivity findClkedActivity(const Pin *pin);
|
PwrActivity findClkedActivity(const Pin *pin);
|
||||||
|
void reportActivityAnnotation(bool report_unannotated,
|
||||||
|
bool report_annotated);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool inClockNetwork(const Instance *inst);
|
bool inClockNetwork(const Instance *inst);
|
||||||
|
|
@ -199,6 +201,9 @@ protected:
|
||||||
const Instance *inst);
|
const Instance *inst);
|
||||||
float evalBddDuty(DdNode *bdd,
|
float evalBddDuty(DdNode *bdd,
|
||||||
const Instance *inst);
|
const Instance *inst);
|
||||||
|
void findUnannotatedPins(const Instance *inst,
|
||||||
|
PinSeq &unannotated_pins);
|
||||||
|
size_t pinCount();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Port/pin activities set by set_pin_activity.
|
// Port/pin activities set by set_pin_activity.
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
#include "Sta.hh"
|
#include "Sta.hh"
|
||||||
#include "power/Power.hh"
|
#include "power/Power.hh"
|
||||||
#include "power/VcdReader.hh"
|
#include "power/VcdReader.hh"
|
||||||
#include "power/ReadVcdActivities.hh"
|
|
||||||
#include "power/SaifReader.hh"
|
#include "power/SaifReader.hh"
|
||||||
|
|
||||||
using namespace sta;
|
using namespace sta;
|
||||||
|
|
@ -113,22 +112,6 @@ read_vcd_file(const char *filename,
|
||||||
readVcdActivities(filename, scope, sta);
|
readVcdActivities(filename, scope, sta);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
report_vcd_waveforms(const char *filename)
|
|
||||||
{
|
|
||||||
Sta *sta = Sta::sta();
|
|
||||||
reportVcdWaveforms(filename, sta);
|
|
||||||
}
|
|
||||||
|
|
||||||
// debugging
|
|
||||||
void
|
|
||||||
report_vcd_var_values(const char *filename,
|
|
||||||
const char *var_name)
|
|
||||||
{
|
|
||||||
Sta *sta = Sta::sta();
|
|
||||||
reportVcdVarValues(filename, var_name, sta);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -140,4 +123,13 @@ read_saif_file(const char *filename,
|
||||||
return readSaif(filename, scope, sta);
|
return readSaif(filename, scope, sta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
report_activity_annotation_cmd(bool report_unannotated,
|
||||||
|
bool report_annotated)
|
||||||
|
{
|
||||||
|
Power *power = Sta::sta()->power();
|
||||||
|
power->reportActivityAnnotation(report_unannotated,
|
||||||
|
report_annotated);
|
||||||
|
}
|
||||||
|
|
||||||
%} // inline
|
%} // inline
|
||||||
|
|
|
||||||
|
|
@ -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 { } {
|
proc power_find_nan { } {
|
||||||
set corner [cmd_corner]
|
set corner [cmd_corner]
|
||||||
foreach inst [network_leaf_instances] {
|
foreach inst [network_leaf_instances] {
|
||||||
|
|
|
||||||
|
|
@ -1,258 +0,0 @@
|
||||||
// OpenSTA, Static Timing Analyzer
|
|
||||||
// Copyright (c) 2024, Parallax Software, Inc.
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include "ReadVcdActivities.hh"
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "VcdReader.hh"
|
|
||||||
#include "Debug.hh"
|
|
||||||
#include "Network.hh"
|
|
||||||
#include "VerilogNamespace.hh"
|
|
||||||
#include "ParseBus.hh"
|
|
||||||
#include "Sdc.hh"
|
|
||||||
#include "Power.hh"
|
|
||||||
#include "Sta.hh"
|
|
||||||
|
|
||||||
namespace sta {
|
|
||||||
|
|
||||||
using std::abs;
|
|
||||||
using std::min;
|
|
||||||
using std::to_string;
|
|
||||||
|
|
||||||
class ReadVcdActivities : public StaState
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ReadVcdActivities(const char *filename,
|
|
||||||
const char *scope,
|
|
||||||
Sta *sta);
|
|
||||||
void readActivities();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void setActivities();
|
|
||||||
void setVarActivity(VcdVar *var,
|
|
||||||
string &var_name,
|
|
||||||
const VcdValues &var_value);
|
|
||||||
void setVarActivity(const char *pin_name,
|
|
||||||
const VcdValues &var_values,
|
|
||||||
int value_bit);
|
|
||||||
void findVarActivity(const VcdValues &var_values,
|
|
||||||
int value_bit,
|
|
||||||
// Return values.
|
|
||||||
double &transition_count,
|
|
||||||
double &activity,
|
|
||||||
double &duty);
|
|
||||||
void checkClkPeriod(const Pin *pin,
|
|
||||||
double transition_count);
|
|
||||||
|
|
||||||
const char *filename_;
|
|
||||||
const char *scope_;
|
|
||||||
Vcd vcd_;
|
|
||||||
double clk_period_;
|
|
||||||
Sta *sta_;
|
|
||||||
Power *power_;
|
|
||||||
std::set<const Pin*> annotated_pins_;
|
|
||||||
|
|
||||||
static constexpr double sim_clk_period_tolerance_ = .1;
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
readVcdActivities(const char *filename,
|
|
||||||
const char *scope,
|
|
||||||
Sta *sta)
|
|
||||||
{
|
|
||||||
ReadVcdActivities reader(filename, scope, sta);
|
|
||||||
reader.readActivities();
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadVcdActivities::ReadVcdActivities(const char *filename,
|
|
||||||
const char *scope,
|
|
||||||
Sta *sta) :
|
|
||||||
StaState(sta),
|
|
||||||
filename_(filename),
|
|
||||||
scope_(scope),
|
|
||||||
vcd_(sta),
|
|
||||||
clk_period_(0.0),
|
|
||||||
sta_(sta),
|
|
||||||
power_(sta->power())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ReadVcdActivities::readActivities()
|
|
||||||
{
|
|
||||||
vcd_ = readVcdFile(filename_, sta_);
|
|
||||||
|
|
||||||
clk_period_ = INF;
|
|
||||||
for (Clock *clk : *sta_->sdc()->clocks())
|
|
||||||
clk_period_ = min(static_cast<double>(clk->period()), clk_period_);
|
|
||||||
|
|
||||||
if (vcd_.timeMax() > 0)
|
|
||||||
setActivities();
|
|
||||||
else
|
|
||||||
report_->warn(1450, "VCD max time is zero.");
|
|
||||||
report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ReadVcdActivities::setActivities()
|
|
||||||
{
|
|
||||||
size_t scope_length = strlen(scope_);
|
|
||||||
for (VcdVar *var : vcd_.vars()) {
|
|
||||||
const VcdValues &var_values = vcd_.values(var);
|
|
||||||
if (!var_values.empty()
|
|
||||||
&& (var->type() == VcdVarType::wire
|
|
||||||
|| var->type() == VcdVarType::reg)) {
|
|
||||||
string var_name = var->name();
|
|
||||||
// string::starts_with in c++20
|
|
||||||
if (scope_length) {
|
|
||||||
if (var_name.substr(0, scope_length) == scope_) {
|
|
||||||
var_name = var_name.substr(scope_length + 1);
|
|
||||||
setVarActivity(var, var_name, var_values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
setVarActivity(var, var_name, var_values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ReadVcdActivities::setVarActivity(VcdVar *var,
|
|
||||||
string &var_name,
|
|
||||||
const VcdValues &var_values)
|
|
||||||
{
|
|
||||||
if (var->width() == 1) {
|
|
||||||
string sta_name = netVerilogToSta(var_name.c_str());
|
|
||||||
setVarActivity(sta_name.c_str(), var_values, 0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
bool is_bus, is_range, subscript_wild;
|
|
||||||
string bus_name;
|
|
||||||
int from, to;
|
|
||||||
parseBusName(var_name.c_str(), '[', ']', '\\',
|
|
||||||
is_bus, is_range, bus_name, from, to, subscript_wild);
|
|
||||||
if (is_bus) {
|
|
||||||
string sta_bus_name = netVerilogToSta(bus_name.c_str());
|
|
||||||
int value_bit = 0;
|
|
||||||
if (to < from) {
|
|
||||||
for (int bus_bit = to; bus_bit <= from; bus_bit++) {
|
|
||||||
string pin_name = sta_bus_name;
|
|
||||||
pin_name += '[';
|
|
||||||
pin_name += to_string(bus_bit);
|
|
||||||
pin_name += ']';
|
|
||||||
setVarActivity(pin_name.c_str(), var_values, value_bit);
|
|
||||||
value_bit++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (int bus_bit = to; bus_bit >= from; bus_bit--) {
|
|
||||||
string pin_name = sta_bus_name;
|
|
||||||
pin_name += '[';
|
|
||||||
pin_name += to_string(bus_bit);
|
|
||||||
pin_name += ']';
|
|
||||||
setVarActivity(pin_name.c_str(), var_values, value_bit);
|
|
||||||
value_bit++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
report_->warn(1451, "problem parsing bus %s.", var_name.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ReadVcdActivities::setVarActivity(const char *pin_name,
|
|
||||||
const VcdValues &var_values,
|
|
||||||
int value_bit)
|
|
||||||
{
|
|
||||||
const Pin *pin = sdc_network_->findPin(pin_name);
|
|
||||||
if (pin) {
|
|
||||||
debugPrint(debug_, "read_vcd_activities", 3, "%s values", pin_name);
|
|
||||||
double transition_count, activity, duty;
|
|
||||||
findVarActivity(var_values, value_bit,
|
|
||||||
transition_count, activity, duty);
|
|
||||||
debugPrint(debug_, "read_vcd_activities", 1,
|
|
||||||
"%s transitions %.1f activity %.2f duty %.2f",
|
|
||||||
pin_name,
|
|
||||||
transition_count,
|
|
||||||
activity,
|
|
||||||
duty);
|
|
||||||
if (sdc_->isLeafPinClock(pin))
|
|
||||||
checkClkPeriod(pin, transition_count);
|
|
||||||
power_->setUserActivity(pin, activity, duty, PwrActivityOrigin::vcd);
|
|
||||||
annotated_pins_.insert(pin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ReadVcdActivities::findVarActivity(const VcdValues &var_values,
|
|
||||||
int value_bit,
|
|
||||||
// Return values.
|
|
||||||
double &transition_count,
|
|
||||||
double &activity,
|
|
||||||
double &duty)
|
|
||||||
{
|
|
||||||
transition_count = 0.0;
|
|
||||||
char prev_value = var_values[0].value(value_bit);
|
|
||||||
VcdTime prev_time = var_values[0].time();
|
|
||||||
VcdTime high_time = 0;
|
|
||||||
for (const VcdValue &var_value : var_values) {
|
|
||||||
VcdTime time = var_value.time();
|
|
||||||
char value = var_value.value(value_bit);
|
|
||||||
debugPrint(debug_, "read_vcd_activities", 3, " %" PRId64 " %c", time, value);
|
|
||||||
if (prev_value == '1')
|
|
||||||
high_time += time - prev_time;
|
|
||||||
if (value != prev_value)
|
|
||||||
transition_count += (value == 'X'
|
|
||||||
|| value == 'Z'
|
|
||||||
|| prev_value == 'X'
|
|
||||||
|| prev_value == 'Z')
|
|
||||||
? .5
|
|
||||||
: 1.0;
|
|
||||||
prev_time = time;
|
|
||||||
prev_value = value;
|
|
||||||
}
|
|
||||||
VcdTime time_max = vcd_.timeMax();
|
|
||||||
if (prev_value == '1')
|
|
||||||
high_time += time_max - prev_time;
|
|
||||||
duty = static_cast<double>(high_time) / time_max;
|
|
||||||
activity = transition_count / (time_max * vcd_.timeScale() / clk_period_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ReadVcdActivities::checkClkPeriod(const Pin *pin,
|
|
||||||
double transition_count)
|
|
||||||
{
|
|
||||||
VcdTime time_max = vcd_.timeMax();
|
|
||||||
double sim_period = time_max * vcd_.timeScale() / (transition_count / 2.0);
|
|
||||||
|
|
||||||
ClockSet *clks = sdc_->findLeafPinClocks(pin);
|
|
||||||
if (clks) {
|
|
||||||
for (Clock *clk : *clks) {
|
|
||||||
double clk_period = clk->period();
|
|
||||||
if (abs((clk_period - sim_period) / clk_period) > .1)
|
|
||||||
// Warn if sim clock period differs from SDC by 10%.
|
|
||||||
report_->warn(1452, "clock %s vcd period %s differs from SDC clock period %s",
|
|
||||||
clk->name(),
|
|
||||||
delayAsString(sim_period, this),
|
|
||||||
delayAsString(clk_period, this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
// OpenSTA, Static Timing Analyzer
|
|
||||||
// Copyright (c) 2024, Parallax Software, Inc.
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace sta {
|
|
||||||
|
|
||||||
class Sta;
|
|
||||||
|
|
||||||
void
|
|
||||||
readVcdActivities(const char *filename,
|
|
||||||
const char *scope,
|
|
||||||
Sta *sta);
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include "Debug.hh"
|
#include "Debug.hh"
|
||||||
#include "Report.hh"
|
#include "Report.hh"
|
||||||
#include "Network.hh"
|
#include "Network.hh"
|
||||||
|
#include "PortDirection.hh"
|
||||||
#include "Sdc.hh"
|
#include "Sdc.hh"
|
||||||
#include "Power.hh"
|
#include "Power.hh"
|
||||||
#include "power/SaifReaderPvt.hh"
|
#include "power/SaifReaderPvt.hh"
|
||||||
|
|
@ -178,7 +179,9 @@ SaifReader::setNetDurations(const char *net_name,
|
||||||
if (parent) {
|
if (parent) {
|
||||||
string unescaped_name = unescaped(net_name);
|
string unescaped_name = unescaped(net_name);
|
||||||
const Pin *pin = sdc_network_->findPin(parent, unescaped_name.c_str());
|
const Pin *pin = sdc_network_->findPin(parent, unescaped_name.c_str());
|
||||||
if (pin) {
|
if (pin
|
||||||
|
&& !sdc_network_->isHierarchical(pin)
|
||||||
|
&& !sdc_network_->direction(pin)->isInternal()) {
|
||||||
double t1 = durations[static_cast<int>(SaifState::T1)];
|
double t1 = durations[static_cast<int>(SaifState::T1)];
|
||||||
float duty = t1 / duration_;
|
float duty = t1 / duration_;
|
||||||
double tc = durations[static_cast<int>(SaifState::TC)];
|
double tc = durations[static_cast<int>(SaifState::TC)];
|
||||||
|
|
|
||||||
213
power/Vcd.cc
213
power/Vcd.cc
|
|
@ -1,213 +0,0 @@
|
||||||
// OpenSTA, Static Timing Analyzer
|
|
||||||
// Copyright (c) 2024, Parallax Software, Inc.
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#include "Vcd.hh"
|
|
||||||
|
|
||||||
#include "Report.hh"
|
|
||||||
|
|
||||||
namespace sta {
|
|
||||||
|
|
||||||
Vcd::Vcd(StaState *sta) :
|
|
||||||
StaState(sta),
|
|
||||||
time_scale_(1.0),
|
|
||||||
time_unit_scale_(1.0),
|
|
||||||
max_var_name_length_(0),
|
|
||||||
max_var_width_(0),
|
|
||||||
min_delta_time_(0),
|
|
||||||
time_max_(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Vcd::Vcd(const Vcd &vcd) :
|
|
||||||
StaState(vcd),
|
|
||||||
date_(vcd.date_),
|
|
||||||
comment_(vcd.comment_),
|
|
||||||
version_(vcd.version_),
|
|
||||||
time_scale_(vcd.time_scale_),
|
|
||||||
time_unit_(vcd.time_unit_),
|
|
||||||
time_unit_scale_(vcd.time_unit_scale_),
|
|
||||||
vars_(vcd.vars_),
|
|
||||||
var_name_map_(vcd.var_name_map_),
|
|
||||||
max_var_name_length_(vcd.max_var_name_length_),
|
|
||||||
max_var_width_(vcd.max_var_width_),
|
|
||||||
id_values_map_(vcd.id_values_map_),
|
|
||||||
min_delta_time_(vcd.min_delta_time_),
|
|
||||||
time_max_(vcd.time_max_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Vcd&
|
|
||||||
Vcd::operator=(Vcd &&vcd1)
|
|
||||||
{
|
|
||||||
date_ = vcd1.date_;
|
|
||||||
comment_ = vcd1.comment_;
|
|
||||||
version_ = vcd1.version_;
|
|
||||||
time_scale_ = vcd1.time_scale_;
|
|
||||||
time_unit_ = vcd1.time_unit_;
|
|
||||||
time_unit_scale_ = vcd1.time_unit_scale_;
|
|
||||||
vars_ = vcd1.vars_;
|
|
||||||
var_name_map_ = vcd1.var_name_map_;
|
|
||||||
max_var_name_length_ = vcd1.max_var_name_length_;
|
|
||||||
max_var_width_ = vcd1.max_var_width_;
|
|
||||||
id_values_map_ = vcd1.id_values_map_;
|
|
||||||
min_delta_time_ = vcd1.min_delta_time_;
|
|
||||||
time_max_ = vcd1.time_max_;
|
|
||||||
|
|
||||||
vcd1.vars_.clear();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vcd::~Vcd()
|
|
||||||
{
|
|
||||||
for (VcdVar *var : vars_)
|
|
||||||
delete var;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Vcd::setTimeUnit(const string &time_unit,
|
|
||||||
double time_unit_scale)
|
|
||||||
{
|
|
||||||
time_unit_ = time_unit;
|
|
||||||
time_unit_scale_ = time_unit_scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Vcd::setDate(const string &date)
|
|
||||||
{
|
|
||||||
date_ = date;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Vcd::setComment(const string &comment)
|
|
||||||
{
|
|
||||||
comment_ = comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Vcd::setVersion(const string &version)
|
|
||||||
{
|
|
||||||
version_ = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Vcd::setTimeScale(double time_scale)
|
|
||||||
{
|
|
||||||
time_scale_ = time_scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Vcd::setMinDeltaTime(VcdTime min_delta_time)
|
|
||||||
{
|
|
||||||
min_delta_time_ = min_delta_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Vcd::setTimeMax(VcdTime time_max)
|
|
||||||
{
|
|
||||||
time_max_ = time_max;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Vcd::makeVar(string &name,
|
|
||||||
VcdVarType type,
|
|
||||||
int width,
|
|
||||||
string &id)
|
|
||||||
{
|
|
||||||
VcdVar *var = new VcdVar(name, type, width, id);
|
|
||||||
vars_.push_back(var);
|
|
||||||
var_name_map_[name] = var;
|
|
||||||
max_var_name_length_ = std::max(max_var_name_length_, name.size());
|
|
||||||
max_var_width_ = std::max(max_var_width_, width);
|
|
||||||
// Make entry for var ID.
|
|
||||||
id_values_map_[id].clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
VcdVar *
|
|
||||||
Vcd::var(const string name)
|
|
||||||
{
|
|
||||||
return var_name_map_[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
Vcd::varIdValid(string &id)
|
|
||||||
{
|
|
||||||
return id_values_map_.find(id) != id_values_map_.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Vcd::varAppendValue(string &id,
|
|
||||||
VcdTime time,
|
|
||||||
char value)
|
|
||||||
{
|
|
||||||
VcdValues &values = id_values_map_[id];
|
|
||||||
values.emplace_back(time, value, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Vcd::varAppendBusValue(string &id,
|
|
||||||
VcdTime time,
|
|
||||||
int64_t bus_value)
|
|
||||||
{
|
|
||||||
VcdValues &values = id_values_map_[id];
|
|
||||||
values.emplace_back(time, '\0', bus_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
VcdValues &
|
|
||||||
Vcd::values(VcdVar *var)
|
|
||||||
{
|
|
||||||
if (id_values_map_.find(var->id()) == id_values_map_.end()) {
|
|
||||||
report_->error(1360, "Unknown variable %s ID %s",
|
|
||||||
var->name().c_str(),
|
|
||||||
var->id().c_str());
|
|
||||||
static VcdValues empty;
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return id_values_map_[var->id()];
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
VcdVar::VcdVar(string name,
|
|
||||||
VcdVarType type,
|
|
||||||
int width,
|
|
||||||
string id) :
|
|
||||||
name_(name),
|
|
||||||
type_(type),
|
|
||||||
width_(width),
|
|
||||||
id_(id)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
VcdValue::VcdValue(VcdTime time,
|
|
||||||
char value,
|
|
||||||
uint64_t bus_value) :
|
|
||||||
time_(time),
|
|
||||||
value_(value),
|
|
||||||
bus_value_(bus_value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
char
|
|
||||||
VcdValue::value(int value_bit) const
|
|
||||||
{
|
|
||||||
if (value_ == '\0')
|
|
||||||
return ((bus_value_ >> value_bit) & 0x1) ? '1' : '0';
|
|
||||||
else
|
|
||||||
return value_;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
158
power/Vcd.hh
158
power/Vcd.hh
|
|
@ -1,158 +0,0 @@
|
||||||
// OpenSTA, Static Timing Analyzer
|
|
||||||
// Copyright (c) 2024, Parallax Software, Inc.
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "StaState.hh"
|
|
||||||
|
|
||||||
namespace sta {
|
|
||||||
|
|
||||||
using std::string;
|
|
||||||
using std::vector;
|
|
||||||
using std::map;
|
|
||||||
using std::max;
|
|
||||||
using std::min;
|
|
||||||
|
|
||||||
class VcdVar;
|
|
||||||
class VcdValue;
|
|
||||||
typedef vector<VcdValue> VcdValues;
|
|
||||||
typedef int64_t VcdTime;
|
|
||||||
typedef vector<string> VcdScope;
|
|
||||||
typedef map<string, VcdVar*> VcdNameMap;
|
|
||||||
|
|
||||||
enum class VcdVarType {
|
|
||||||
wire,
|
|
||||||
reg,
|
|
||||||
parameter,
|
|
||||||
integer,
|
|
||||||
real,
|
|
||||||
supply0,
|
|
||||||
supply1,
|
|
||||||
time,
|
|
||||||
tri,
|
|
||||||
triand,
|
|
||||||
trior,
|
|
||||||
trireg,
|
|
||||||
tri0,
|
|
||||||
tri1,
|
|
||||||
wand,
|
|
||||||
wor,
|
|
||||||
unknown
|
|
||||||
};
|
|
||||||
|
|
||||||
class Vcd : public StaState
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Vcd(StaState *sta);
|
|
||||||
Vcd(const Vcd &vcd);
|
|
||||||
// Move copy assignment.
|
|
||||||
Vcd& operator=(Vcd &&vcd1);
|
|
||||||
~Vcd();
|
|
||||||
VcdVar *var(const string name);
|
|
||||||
VcdValues &values(VcdVar *var);
|
|
||||||
|
|
||||||
const string &date() const { return date_; }
|
|
||||||
void setDate(const string &date);
|
|
||||||
const string &comment() const { return comment_; }
|
|
||||||
void setComment(const string &comment);
|
|
||||||
const string &version() const { return version_; }
|
|
||||||
void setVersion(const string &version);
|
|
||||||
double timeScale() const { return time_scale_; }
|
|
||||||
void setTimeScale(double time_scale);
|
|
||||||
const string &timeUnit() const { return time_unit_; }
|
|
||||||
double timeUnitScale() const { return time_unit_scale_; }
|
|
||||||
void setTimeUnit(const string &time_unit,
|
|
||||||
double time_unit_scale);
|
|
||||||
VcdTime timeMax() const { return time_max_; }
|
|
||||||
void setTimeMax(VcdTime time_max);
|
|
||||||
VcdTime minDeltaTime() const { return min_delta_time_; }
|
|
||||||
void setMinDeltaTime(VcdTime min_delta_time);
|
|
||||||
vector<VcdVar*> vars() { return vars_; }
|
|
||||||
void makeVar(string &name,
|
|
||||||
VcdVarType type,
|
|
||||||
int width,
|
|
||||||
string &id);
|
|
||||||
int maxVarWidth() const { return max_var_width_; }
|
|
||||||
int maxVarNameLength() const { return max_var_name_length_; }
|
|
||||||
bool varIdValid(string &id);
|
|
||||||
void varAppendValue(string &id,
|
|
||||||
VcdTime time,
|
|
||||||
char value);
|
|
||||||
void varAppendBusValue(string &id,
|
|
||||||
VcdTime time,
|
|
||||||
int64_t bus_value);
|
|
||||||
|
|
||||||
private:
|
|
||||||
string date_;
|
|
||||||
string comment_;
|
|
||||||
string version_;
|
|
||||||
double time_scale_;
|
|
||||||
string time_unit_;
|
|
||||||
double time_unit_scale_;
|
|
||||||
|
|
||||||
vector<VcdVar*> vars_;
|
|
||||||
VcdNameMap var_name_map_;
|
|
||||||
size_t max_var_name_length_;
|
|
||||||
int max_var_width_;
|
|
||||||
map<string, VcdValues> id_values_map_;
|
|
||||||
VcdTime min_delta_time_;
|
|
||||||
VcdTime time_max_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VcdVar
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
VcdVar(string name,
|
|
||||||
VcdVarType type,
|
|
||||||
int width,
|
|
||||||
string id);
|
|
||||||
const string& name() const { return name_; }
|
|
||||||
VcdVarType type() const { return type_; }
|
|
||||||
int width() const { return width_; }
|
|
||||||
const string& id() const { return id_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
string name_;
|
|
||||||
VcdVarType type_;
|
|
||||||
int width_;
|
|
||||||
string id_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VcdValue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
VcdValue(VcdTime time,
|
|
||||||
char value,
|
|
||||||
uint64_t bus_value);
|
|
||||||
VcdTime time() const { return time_; }
|
|
||||||
char value() const { return value_; }
|
|
||||||
uint64_t busValue() const { return bus_value_; }
|
|
||||||
char value(int value_bit) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
VcdTime time_;
|
|
||||||
// 01XUZ or '\0' when width > 1 to use bus_value_.
|
|
||||||
char value_;
|
|
||||||
uint64_t bus_value_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
@ -0,0 +1,318 @@
|
||||||
|
// OpenSTA, Static Timing Analyzer
|
||||||
|
// Copyright (c) 2024, Parallax Software, Inc.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "VcdParse.hh"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
|
#include "Stats.hh"
|
||||||
|
#include "Report.hh"
|
||||||
|
#include "Error.hh"
|
||||||
|
#include "EnumNameMap.hh"
|
||||||
|
|
||||||
|
namespace sta {
|
||||||
|
|
||||||
|
using std::isspace;
|
||||||
|
|
||||||
|
// Very imprecise syntax definition
|
||||||
|
// https://en.wikipedia.org/wiki/Value_change_dump#Structure.2FSyntax
|
||||||
|
// Much better syntax definition
|
||||||
|
// https://web.archive.org/web/20120323132708/http://www.beyondttl.com/vcd.php
|
||||||
|
|
||||||
|
void
|
||||||
|
VcdParse::read(const char *filename,
|
||||||
|
VcdReader *reader)
|
||||||
|
{
|
||||||
|
stream_ = gzopen(filename, "r");
|
||||||
|
if (stream_) {
|
||||||
|
Stats stats(debug_, report_);
|
||||||
|
filename_ = filename;
|
||||||
|
reader_ = reader;
|
||||||
|
file_line_ = 1;
|
||||||
|
stmt_line_ = 1;
|
||||||
|
string token = getToken();
|
||||||
|
while (!token.empty()) {
|
||||||
|
if (token == "$date")
|
||||||
|
reader_->setDate(readStmtString());
|
||||||
|
else if (token == "$comment")
|
||||||
|
reader_->setComment(readStmtString());
|
||||||
|
else if (token == "$version")
|
||||||
|
reader_->setVersion(readStmtString());
|
||||||
|
else if (token == "$timescale")
|
||||||
|
parseTimescale();
|
||||||
|
else if (token == "$var")
|
||||||
|
parseVar();
|
||||||
|
else if (token == "$scope")
|
||||||
|
parseScope();
|
||||||
|
else if (token == "$upscope")
|
||||||
|
parseUpscope();
|
||||||
|
else if (token == "$enddefinitions")
|
||||||
|
// empty body
|
||||||
|
readStmtString();
|
||||||
|
else if (token == "$dumpall")
|
||||||
|
parseVarValues();
|
||||||
|
else if (token == "$dumpvars")
|
||||||
|
// Initial values.
|
||||||
|
parseVarValues();
|
||||||
|
else if (token[0] == '$')
|
||||||
|
report_->fileError(800, filename_, stmt_line_, "unhandled vcd command.");
|
||||||
|
else
|
||||||
|
parseVarValues();
|
||||||
|
token = getToken();
|
||||||
|
}
|
||||||
|
gzclose(stream_);
|
||||||
|
stats.report("Read VCD");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw FileNotReadable(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
VcdParse::VcdParse(Report *report,
|
||||||
|
Debug *debug) :
|
||||||
|
reader_(nullptr),
|
||||||
|
file_line_(0),
|
||||||
|
stmt_line_(0),
|
||||||
|
time_(0),
|
||||||
|
prev_time_(0),
|
||||||
|
report_(report),
|
||||||
|
debug_(debug)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VcdParse::parseTimescale()
|
||||||
|
{
|
||||||
|
vector<string> tokens = readStmtTokens();
|
||||||
|
if (tokens.size() == 1) {
|
||||||
|
size_t last;
|
||||||
|
double time_scale = std::stod(tokens[0], &last);
|
||||||
|
setTimeUnit(tokens[0].substr(last), time_scale);
|
||||||
|
}
|
||||||
|
else if (tokens.size() == 2) {
|
||||||
|
double time_scale = std::stod(tokens[0]);
|
||||||
|
setTimeUnit(tokens[1], time_scale);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
report_->fileError(801, filename_, stmt_line_, "timescale syntax error.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VcdParse::setTimeUnit(const string &time_unit,
|
||||||
|
double time_scale)
|
||||||
|
{
|
||||||
|
double time_unit_scale = 1.0;
|
||||||
|
if (time_unit == "fs")
|
||||||
|
time_unit_scale = 1e-15;
|
||||||
|
else if (time_unit == "ps")
|
||||||
|
time_unit_scale = 1e-12;
|
||||||
|
else if (time_unit == "ns")
|
||||||
|
time_unit_scale = 1e-9;
|
||||||
|
else
|
||||||
|
report_->fileError(802, filename_, stmt_line_, "Unknown timescale unit.");
|
||||||
|
reader_->setTimeUnit(time_unit, time_unit_scale, time_scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
static EnumNameMap<VcdVarType> vcd_var_type_map =
|
||||||
|
{{VcdVarType::wire, "wire"},
|
||||||
|
{VcdVarType::reg, "reg"},
|
||||||
|
{VcdVarType::parameter, "parameter"},
|
||||||
|
{VcdVarType::integer, "integer"},
|
||||||
|
{VcdVarType::real, "real"},
|
||||||
|
{VcdVarType::supply0, "supply0"},
|
||||||
|
{VcdVarType::supply1, "supply1"},
|
||||||
|
{VcdVarType::time, "time"},
|
||||||
|
{VcdVarType::tri, "tri"},
|
||||||
|
{VcdVarType::triand, "triand"},
|
||||||
|
{VcdVarType::trior, "trior"},
|
||||||
|
{VcdVarType::trireg, "trireg"},
|
||||||
|
{VcdVarType::tri0, "tri0"},
|
||||||
|
{VcdVarType::tri1, "tri1"},
|
||||||
|
{VcdVarType::wand, "wand"},
|
||||||
|
{VcdVarType::wor, "wor"}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
VcdParse::parseVar()
|
||||||
|
{
|
||||||
|
vector<string> tokens = readStmtTokens();
|
||||||
|
if (tokens.size() == 4
|
||||||
|
|| tokens.size() == 5) {
|
||||||
|
string type_name = tokens[0];
|
||||||
|
VcdVarType type = vcd_var_type_map.find(type_name, VcdVarType::unknown);
|
||||||
|
if (type == VcdVarType::unknown)
|
||||||
|
report_->fileWarn(1370, filename_, stmt_line_,
|
||||||
|
"Unknown variable type %s.",
|
||||||
|
type_name.c_str());
|
||||||
|
else {
|
||||||
|
size_t width = stoi(tokens[1]);
|
||||||
|
string &id = tokens[2];
|
||||||
|
string name = tokens[3];
|
||||||
|
// iverilog separates bus base name from bit range.
|
||||||
|
if (tokens.size() == 5) {
|
||||||
|
// Preserve space after esacaped name.
|
||||||
|
if (name[0] == '\\')
|
||||||
|
name += ' ';
|
||||||
|
name += tokens[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_->makeVar(scope_, name, type, width, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
report_->fileError(804, filename_, stmt_line_, "Variable syntax error.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VcdParse::parseScope()
|
||||||
|
{
|
||||||
|
vector<string> tokens = readStmtTokens();
|
||||||
|
string &scope = tokens[1];
|
||||||
|
scope_.push_back(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VcdParse::parseUpscope()
|
||||||
|
{
|
||||||
|
readStmtTokens();
|
||||||
|
scope_.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VcdParse::parseVarValues()
|
||||||
|
{
|
||||||
|
string token = getToken();
|
||||||
|
while (!token.empty()) {
|
||||||
|
char char0 = toupper(token[0]);
|
||||||
|
if (char0 == '#' && token.size() > 1) {
|
||||||
|
prev_time_ = time_;
|
||||||
|
time_ = stoll(token.substr(1));
|
||||||
|
if (time_ > prev_time_)
|
||||||
|
reader_->varMinDeltaTime(time_ - prev_time_);
|
||||||
|
}
|
||||||
|
else if (char0 == '0'
|
||||||
|
|| char0 == '1'
|
||||||
|
|| char0 == 'X'
|
||||||
|
|| char0 == 'U'
|
||||||
|
|| char0 == 'Z') {
|
||||||
|
string id = token.substr(1);
|
||||||
|
if (!reader_->varIdValid(id))
|
||||||
|
report_->fileError(805, filename_, stmt_line_,
|
||||||
|
"unknown variable %s", id.c_str());
|
||||||
|
reader_->varAppendValue(id, time_, char0);
|
||||||
|
}
|
||||||
|
else if (char0 == 'B') {
|
||||||
|
char char1 = toupper(token[1]);
|
||||||
|
if (char1 == 'X'
|
||||||
|
|| char1 == 'U'
|
||||||
|
|| char1 == 'Z') {
|
||||||
|
string id = getToken();
|
||||||
|
if (!reader_->varIdValid(id))
|
||||||
|
report_->fileError(806, filename_, stmt_line_,
|
||||||
|
"unknown variable %s", id.c_str());
|
||||||
|
// Bus mixed 0/1/X/U not supported.
|
||||||
|
reader_->varAppendValue(id, time_, char1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
string bin = token.substr(1);
|
||||||
|
char *end;
|
||||||
|
int64_t bus_value = strtol(bin.c_str(), &end, 2);
|
||||||
|
string id = getToken();
|
||||||
|
if (!reader_->varIdValid(id))
|
||||||
|
report_->fileError(807, filename_, stmt_line_,
|
||||||
|
"unknown variable %s", id.c_str());
|
||||||
|
else
|
||||||
|
reader_->varAppendBusValue(id, time_, bus_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token = getToken();
|
||||||
|
}
|
||||||
|
reader_->setTimeMax(time_);
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
VcdParse::readStmtString()
|
||||||
|
{
|
||||||
|
stmt_line_ = file_line_;
|
||||||
|
string line;
|
||||||
|
string token = getToken();
|
||||||
|
while (!token.empty() && token != "$end") {
|
||||||
|
if (!line.empty())
|
||||||
|
line += " ";
|
||||||
|
line += token;
|
||||||
|
token = getToken();
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string>
|
||||||
|
VcdParse::readStmtTokens()
|
||||||
|
{
|
||||||
|
stmt_line_ = file_line_;
|
||||||
|
vector<string> tokens;
|
||||||
|
string token = getToken();
|
||||||
|
while (!token.empty() && token != "$end") {
|
||||||
|
tokens.push_back(token);
|
||||||
|
token = getToken();
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
VcdParse::getToken()
|
||||||
|
{
|
||||||
|
string token;
|
||||||
|
int ch = gzgetc(stream_);
|
||||||
|
if (ch == '\n')
|
||||||
|
file_line_++;
|
||||||
|
// skip whitespace
|
||||||
|
while (ch != EOF && isspace(ch)) {
|
||||||
|
ch = gzgetc(stream_);
|
||||||
|
if (ch == '\n')
|
||||||
|
file_line_++;
|
||||||
|
}
|
||||||
|
while (ch != EOF && !isspace(ch)) {
|
||||||
|
token.push_back(ch);
|
||||||
|
ch = gzgetc(stream_);
|
||||||
|
if (ch == '\n')
|
||||||
|
file_line_++;
|
||||||
|
}
|
||||||
|
if (ch == EOF)
|
||||||
|
return "";
|
||||||
|
else
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
char
|
||||||
|
VcdValue::value(int value_bit) const
|
||||||
|
{
|
||||||
|
if (value_ == '\0')
|
||||||
|
return ((bus_value_ >> value_bit) & 0x1) ? '1' : '0';
|
||||||
|
else
|
||||||
|
return value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VcdValue::setValue(VcdTime time,
|
||||||
|
char value)
|
||||||
|
{
|
||||||
|
time_ = time;
|
||||||
|
value_ = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
// OpenSTA, Static Timing Analyzer
|
||||||
|
// Copyright (c) 2024, Parallax Software, Inc.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Zlib.hh"
|
||||||
|
#include "StaState.hh"
|
||||||
|
|
||||||
|
namespace sta {
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
|
typedef int64_t VcdTime;
|
||||||
|
typedef vector<string> VcdScope;
|
||||||
|
|
||||||
|
enum class VcdVarType {
|
||||||
|
wire,
|
||||||
|
reg,
|
||||||
|
parameter,
|
||||||
|
integer,
|
||||||
|
real,
|
||||||
|
supply0,
|
||||||
|
supply1,
|
||||||
|
time,
|
||||||
|
tri,
|
||||||
|
triand,
|
||||||
|
trior,
|
||||||
|
trireg,
|
||||||
|
tri0,
|
||||||
|
tri1,
|
||||||
|
wand,
|
||||||
|
wor,
|
||||||
|
unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
class VcdReader;
|
||||||
|
|
||||||
|
class VcdParse : public StaState
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VcdParse(Report *report,
|
||||||
|
Debug *debug);
|
||||||
|
void read(const char *filename,
|
||||||
|
VcdReader *reader);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void parseTimescale();
|
||||||
|
void setTimeUnit(const string &time_unit,
|
||||||
|
double time_scale);
|
||||||
|
void parseVar();
|
||||||
|
void parseScope();
|
||||||
|
void parseUpscope();
|
||||||
|
void parseVarValues();
|
||||||
|
string getToken();
|
||||||
|
string readStmtString();
|
||||||
|
vector<string> readStmtTokens();
|
||||||
|
|
||||||
|
VcdReader *reader_;
|
||||||
|
gzFile stream_;
|
||||||
|
string token_;
|
||||||
|
const char *filename_;
|
||||||
|
int file_line_;
|
||||||
|
int stmt_line_;
|
||||||
|
|
||||||
|
VcdTime time_;
|
||||||
|
VcdTime prev_time_;
|
||||||
|
VcdScope scope_;
|
||||||
|
|
||||||
|
Report *report_;
|
||||||
|
Debug *debug_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Abstract class for VcdParse callbacks.
|
||||||
|
class VcdReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~VcdReader() {}
|
||||||
|
virtual void setDate(const string &date) = 0;
|
||||||
|
virtual void setComment(const string &comment) = 0;
|
||||||
|
virtual void setVersion(const string &version) = 0;
|
||||||
|
virtual void setTimeUnit(const string &time_unit,
|
||||||
|
double time_unit_scale,
|
||||||
|
double time_scale) = 0;
|
||||||
|
virtual void setTimeMax(VcdTime time_max) = 0;
|
||||||
|
virtual void varMinDeltaTime(VcdTime min_delta_time) = 0;
|
||||||
|
virtual bool varIdValid(const string &id) = 0;
|
||||||
|
virtual void makeVar(const VcdScope &scope,
|
||||||
|
const string &name,
|
||||||
|
VcdVarType type,
|
||||||
|
size_t width,
|
||||||
|
const string &id) = 0;
|
||||||
|
virtual void varAppendValue(const string &id,
|
||||||
|
VcdTime time,
|
||||||
|
char value) = 0;
|
||||||
|
virtual void varAppendBusValue(const string &id,
|
||||||
|
VcdTime time,
|
||||||
|
int64_t bus_value) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VcdValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VcdValue();
|
||||||
|
VcdTime time() const { return time_; }
|
||||||
|
char value() const { return value_; }
|
||||||
|
void setValue(VcdTime time,
|
||||||
|
char value);
|
||||||
|
uint64_t busValue() const { return bus_value_; }
|
||||||
|
char value(int value_bit) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
VcdTime time_;
|
||||||
|
// 01XUZ or '\0' when width > 1 to use bus_value_.
|
||||||
|
char value_;
|
||||||
|
uint64_t bus_value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
@ -14,437 +14,433 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
#include <cinttypes>
|
|
||||||
|
|
||||||
#include "VcdReader.hh"
|
#include "VcdReader.hh"
|
||||||
|
|
||||||
#include "Zlib.hh"
|
#include <inttypes.h>
|
||||||
#include "Stats.hh"
|
#include <set>
|
||||||
#include "Report.hh"
|
|
||||||
#include "Error.hh"
|
#include "VcdParse.hh"
|
||||||
#include "StringUtil.hh"
|
#include "Debug.hh"
|
||||||
#include "EnumNameMap.hh"
|
#include "Network.hh"
|
||||||
|
#include "PortDirection.hh"
|
||||||
|
#include "VerilogNamespace.hh"
|
||||||
|
#include "ParseBus.hh"
|
||||||
|
#include "Sdc.hh"
|
||||||
|
#include "Power.hh"
|
||||||
|
#include "Sta.hh"
|
||||||
|
|
||||||
namespace sta {
|
namespace sta {
|
||||||
|
|
||||||
using std::isspace;
|
using std::abs;
|
||||||
|
using std::min;
|
||||||
|
using std::to_string;
|
||||||
|
using std::vector;
|
||||||
|
using std::map;
|
||||||
|
|
||||||
// Very imprecise syntax definition
|
// Transition count and high time for duty cycle for a group of pins
|
||||||
// https://en.wikipedia.org/wiki/Value_change_dump#Structure.2FSyntax
|
// for one bit of vcd ID.
|
||||||
// Much better syntax definition
|
class VcdCount
|
||||||
// https://web.archive.org/web/20120323132708/http://www.beyondttl.com/vcd.php
|
|
||||||
|
|
||||||
class VcdReader : public StaState
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VcdReader(StaState *sta);
|
VcdCount();
|
||||||
Vcd read(const char *filename);
|
double transitionCount() const { return transition_count_; }
|
||||||
|
VcdTime highTime(VcdTime time_max) const;
|
||||||
|
void incrCounts(VcdTime time,
|
||||||
|
char value);
|
||||||
|
void incrCounts(VcdTime time,
|
||||||
|
int64_t value);
|
||||||
|
void addPin(const Pin *pin);
|
||||||
|
const PinSeq &pins() const { return pins_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void parseTimescale();
|
PinSeq pins_;
|
||||||
void setTimeUnit(const string &time_unit);
|
|
||||||
void parseVar();
|
|
||||||
void parseScope();
|
|
||||||
void parseUpscope();
|
|
||||||
void parseVarValues();
|
|
||||||
string getToken();
|
|
||||||
string readStmtString();
|
|
||||||
vector<string> readStmtTokens();
|
|
||||||
|
|
||||||
gzFile stream_;
|
|
||||||
string token_;
|
|
||||||
const char *filename_;
|
|
||||||
int file_line_;
|
|
||||||
int stmt_line_;
|
|
||||||
|
|
||||||
Vcd *vcd_;
|
|
||||||
VcdTime time_;
|
|
||||||
VcdTime prev_time_;
|
VcdTime prev_time_;
|
||||||
VcdScope scope_;
|
char prev_value_;
|
||||||
|
VcdTime high_time_;
|
||||||
|
double transition_count_;
|
||||||
};
|
};
|
||||||
|
|
||||||
Vcd
|
VcdCount::VcdCount() :
|
||||||
readVcdFile(const char *filename,
|
prev_time_(-1),
|
||||||
StaState *sta)
|
prev_value_('\0'),
|
||||||
|
high_time_(0),
|
||||||
{
|
transition_count_(0)
|
||||||
VcdReader reader(sta);
|
|
||||||
return reader.read(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vcd
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
VcdReader::parseTimescale()
|
VcdCount::addPin(const Pin *pin)
|
||||||
{
|
{
|
||||||
vector<string> tokens = readStmtTokens();
|
pins_.push_back(pin);
|
||||||
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
|
void
|
||||||
VcdReader::setTimeUnit(const string &time_unit)
|
VcdCount::incrCounts(VcdTime time,
|
||||||
|
char value)
|
||||||
{
|
{
|
||||||
double time_unit_scale = 1.0;
|
// Initial value does not coontribute to transitions or high time.
|
||||||
if (time_unit == "fs")
|
if (prev_time_ != -1) {
|
||||||
time_unit_scale = 1e-15;
|
if (prev_value_ == '1')
|
||||||
else if (time_unit == "ps")
|
high_time_ += time - prev_time_;
|
||||||
time_unit_scale = 1e-12;
|
if (value != prev_value_)
|
||||||
else if (time_unit == "ns")
|
transition_count_ += (value == 'X'
|
||||||
time_unit_scale = 1e-9;
|
|| 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
|
else
|
||||||
report_->fileError(802, filename_, stmt_line_, "Unknown timescale unit.");
|
return high_time_;
|
||||||
vcd_->setTimeUnit(time_unit, time_unit_scale);;
|
|
||||||
}
|
|
||||||
|
|
||||||
static EnumNameMap<VcdVarType> vcd_var_type_map =
|
|
||||||
{{VcdVarType::wire, "wire"},
|
|
||||||
{VcdVarType::reg, "reg"},
|
|
||||||
{VcdVarType::parameter, "parameter"},
|
|
||||||
{VcdVarType::integer, "integer"},
|
|
||||||
{VcdVarType::real, "real"},
|
|
||||||
{VcdVarType::supply0, "supply0"},
|
|
||||||
{VcdVarType::supply1, "supply1"},
|
|
||||||
{VcdVarType::time, "time"},
|
|
||||||
{VcdVarType::tri, "tri"},
|
|
||||||
{VcdVarType::triand, "triand"},
|
|
||||||
{VcdVarType::trior, "trior"},
|
|
||||||
{VcdVarType::trireg, "trireg"},
|
|
||||||
{VcdVarType::tri0, "tri0"},
|
|
||||||
{VcdVarType::tri1, "tri1"},
|
|
||||||
{VcdVarType::wand, "wand"},
|
|
||||||
{VcdVarType::wor, "wor"}
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
VcdReader::parseVar()
|
|
||||||
{
|
|
||||||
vector<string> tokens = readStmtTokens();
|
|
||||||
if (tokens.size() == 4
|
|
||||||
|| tokens.size() == 5) {
|
|
||||||
string type_name = tokens[0];
|
|
||||||
VcdVarType type = vcd_var_type_map.find(type_name, VcdVarType::unknown);
|
|
||||||
if (type == VcdVarType::unknown)
|
|
||||||
report_->fileWarn(1370, filename_, stmt_line_,
|
|
||||||
"Unknown variable type %s.",
|
|
||||||
type_name.c_str());
|
|
||||||
else {
|
|
||||||
int width = stoi(tokens[1]);
|
|
||||||
string &id = tokens[2];
|
|
||||||
string name;
|
|
||||||
|
|
||||||
for (string &context : scope_) {
|
|
||||||
name += context;
|
|
||||||
name += '/';
|
|
||||||
}
|
|
||||||
name += tokens[3];
|
|
||||||
// iverilog separates bus base name from bit range.
|
|
||||||
if (tokens.size() == 5) {
|
|
||||||
// Preserve space after esacaped name.
|
|
||||||
if (name[0] == '\\')
|
|
||||||
name += ' ';
|
|
||||||
name += tokens[4];
|
|
||||||
}
|
|
||||||
|
|
||||||
vcd_->makeVar(name, type, width, id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
report_->fileError(804, filename_, stmt_line_, "Variable syntax error.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VcdReader::parseScope()
|
|
||||||
{
|
|
||||||
vector<string> tokens = readStmtTokens();
|
|
||||||
string &scope = tokens[1];
|
|
||||||
scope_.push_back(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VcdReader::parseUpscope()
|
|
||||||
{
|
|
||||||
readStmtTokens();
|
|
||||||
scope_.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VcdReader::parseVarValues()
|
|
||||||
{
|
|
||||||
string token = getToken();
|
|
||||||
while (!token.empty()) {
|
|
||||||
char char0 = toupper(token[0]);
|
|
||||||
if (char0 == '#' && token.size() > 1) {
|
|
||||||
prev_time_ = time_;
|
|
||||||
time_ = stoll(token.substr(1));
|
|
||||||
if (time_ > prev_time_)
|
|
||||||
vcd_->setMinDeltaTime(min(time_ - prev_time_, vcd_->minDeltaTime()));
|
|
||||||
}
|
|
||||||
else if (char0 == '0'
|
|
||||||
|| char0 == '1'
|
|
||||||
|| char0 == 'X'
|
|
||||||
|| char0 == 'U'
|
|
||||||
|| char0 == 'Z') {
|
|
||||||
string id = token.substr(1);
|
|
||||||
if (!vcd_->varIdValid(id))
|
|
||||||
report_->fileError(805, filename_, stmt_line_,
|
|
||||||
"unknown variable %s", id.c_str());
|
|
||||||
vcd_->varAppendValue(id, time_, char0);
|
|
||||||
}
|
|
||||||
else if (char0 == 'B') {
|
|
||||||
char char1 = toupper(token[1]);
|
|
||||||
if (char1 == 'X'
|
|
||||||
|| char1 == 'U'
|
|
||||||
|| char1 == 'Z') {
|
|
||||||
string id = getToken();
|
|
||||||
if (!vcd_->varIdValid(id))
|
|
||||||
report_->fileError(806, filename_, stmt_line_,
|
|
||||||
"unknown variable %s", id.c_str());
|
|
||||||
// Bus mixed 0/1/X/U not supported.
|
|
||||||
vcd_->varAppendValue(id, time_, char1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
string bin = token.substr(1);
|
|
||||||
char *end;
|
|
||||||
int64_t bus_value = strtol(bin.c_str(), &end, 2);
|
|
||||||
string id = getToken();
|
|
||||||
if (!vcd_->varIdValid(id))
|
|
||||||
report_->fileError(807, filename_, stmt_line_,
|
|
||||||
"unknown variable %s", id.c_str());
|
|
||||||
else
|
|
||||||
vcd_->varAppendBusValue(id, time_, bus_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
token = getToken();
|
|
||||||
}
|
|
||||||
vcd_->setTimeMax(time_);
|
|
||||||
}
|
|
||||||
|
|
||||||
string
|
|
||||||
VcdReader::readStmtString()
|
|
||||||
{
|
|
||||||
stmt_line_ = file_line_;
|
|
||||||
string line;
|
|
||||||
string token = getToken();
|
|
||||||
while (!token.empty() && token != "$end") {
|
|
||||||
if (!line.empty())
|
|
||||||
line += " ";
|
|
||||||
line += token;
|
|
||||||
token = getToken();
|
|
||||||
}
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<string>
|
|
||||||
VcdReader::readStmtTokens()
|
|
||||||
{
|
|
||||||
stmt_line_ = file_line_;
|
|
||||||
vector<string> tokens;
|
|
||||||
string token = getToken();
|
|
||||||
while (!token.empty() && token != "$end") {
|
|
||||||
tokens.push_back(token);
|
|
||||||
token = getToken();
|
|
||||||
}
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
string
|
|
||||||
VcdReader::getToken()
|
|
||||||
{
|
|
||||||
string token;
|
|
||||||
int ch = gzgetc(stream_);
|
|
||||||
if (ch == '\n')
|
|
||||||
file_line_++;
|
|
||||||
// skip whitespace
|
|
||||||
while (ch != EOF && isspace(ch)) {
|
|
||||||
ch = gzgetc(stream_);
|
|
||||||
if (ch == '\n')
|
|
||||||
file_line_++;
|
|
||||||
}
|
|
||||||
while (ch != EOF && !isspace(ch)) {
|
|
||||||
token.push_back(ch);
|
|
||||||
ch = gzgetc(stream_);
|
|
||||||
if (ch == '\n')
|
|
||||||
file_line_++;
|
|
||||||
}
|
|
||||||
if (ch == EOF)
|
|
||||||
return "";
|
|
||||||
else
|
|
||||||
return token;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static void
|
// VcdCount[bit]
|
||||||
reportWaveforms(Vcd &vcd,
|
typedef vector<VcdCount> VcdCounts;
|
||||||
Report *report);
|
// ID -> VcdCount[bit]
|
||||||
|
typedef map<string, VcdCounts> VcdIdCountsMap;
|
||||||
|
|
||||||
|
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
|
void
|
||||||
reportVcdWaveforms(const char *filename,
|
VcdCountReader::setTimeUnit(const string &,
|
||||||
StaState *sta)
|
double time_unit_scale,
|
||||||
|
double time_scale)
|
||||||
{
|
{
|
||||||
Vcd vcd = readVcdFile(filename, sta);
|
time_scale_ = time_scale * time_unit_scale;
|
||||||
reportWaveforms(vcd, sta->report());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
reportWaveforms(Vcd &vcd,
|
VcdCountReader::setTimeMax(VcdTime time_max)
|
||||||
Report *report)
|
|
||||||
{
|
{
|
||||||
report->reportLine("Date: %s", vcd.date().c_str());
|
time_max_ = time_max;
|
||||||
report->reportLine("Timescale: %.2f%s", vcd.timeScale(), vcd.timeUnit().c_str());
|
}
|
||||||
// Characters per time sample.
|
|
||||||
int zoom = (vcd.maxVarWidth() + 7) / 4;
|
bool
|
||||||
int time_delta = vcd.minDeltaTime();
|
VcdCountReader::varIdValid(const string &)
|
||||||
|
{
|
||||||
int max_var_name_length = vcd.maxVarNameLength();
|
return true;
|
||||||
for (VcdVar *var : vcd.vars()) {
|
}
|
||||||
string line;
|
|
||||||
stringPrint(line, " %-*s",
|
void
|
||||||
static_cast<int>(max_var_name_length),
|
VcdCountReader::makeVar(const VcdScope &scope,
|
||||||
var->name().c_str());
|
const string &name,
|
||||||
const VcdValues &var_values = vcd.values(var);
|
VcdVarType type,
|
||||||
if (!var_values.empty()) {
|
size_t width,
|
||||||
size_t value_index = 0;
|
const string &id)
|
||||||
VcdValue var_value = var_values[value_index];
|
{
|
||||||
VcdValue prev_var_value = var_values[value_index];
|
if (type == VcdVarType::wire
|
||||||
VcdTime next_value_time = var_values[value_index + 1].time();
|
|| type == VcdVarType::reg) {
|
||||||
for (double time = 0.0; time < vcd.timeMax(); time += time_delta) {
|
string path_name;
|
||||||
if (time >= next_value_time) {
|
bool first = true;
|
||||||
if (value_index < var_values.size() - 1)
|
for (const string &context : scope) {
|
||||||
value_index++;
|
if (!first)
|
||||||
var_value = var_values[value_index];
|
path_name += '/';
|
||||||
if (value_index < var_values.size())
|
path_name += context;
|
||||||
next_value_time = var_values[value_index + 1].time();
|
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
|
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
|
void
|
||||||
reportVcdVarValues(const char *filename,
|
VcdCountReader::addVarPin(const string &pin_name,
|
||||||
const char *var_name,
|
const string &id,
|
||||||
StaState *sta)
|
size_t width,
|
||||||
|
size_t bit_idx)
|
||||||
{
|
{
|
||||||
Vcd vcd = readVcdFile(filename, sta);
|
const Pin *pin = sdc_network_->findPin(pin_name.c_str());
|
||||||
VcdVar *var = vcd.var(var_name);
|
if (pin
|
||||||
if (var) {
|
&& !sdc_network_->isHierarchical(pin)
|
||||||
Report *report = sta->report();
|
&& !sdc_network_->direction(pin)->isInternal()) {
|
||||||
for (const VcdValue &var_value : vcd.values(var)) {
|
VcdCounts &vcd_counts = vcd_count_map_[id];
|
||||||
double time = var_value.time() * vcd.timeUnitScale();
|
vcd_counts.resize(width);
|
||||||
char value = var_value.value();
|
vcd_counts[bit_idx].addPin(pin);
|
||||||
if (value == '\0')
|
debugPrint(debug_, "read_vcd_activities", 2, "id %s pin %s",
|
||||||
report->reportLine("%.2e %" PRIu64,
|
id.c_str(),
|
||||||
time, var_value.busValue());
|
pin_name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,24 +16,13 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Vcd.hh"
|
|
||||||
|
|
||||||
namespace sta {
|
namespace sta {
|
||||||
|
|
||||||
class StaState;
|
class Sta;
|
||||||
|
|
||||||
Vcd
|
|
||||||
readVcdFile(const char *filename,
|
|
||||||
StaState *sta);
|
|
||||||
|
|
||||||
void
|
void
|
||||||
reportVcdWaveforms(const char *filename,
|
readVcdActivities(const char *filename,
|
||||||
StaState *sta);
|
const char *scope,
|
||||||
|
Sta *sta);
|
||||||
void
|
|
||||||
reportVcdVarValues(const char *filename,
|
|
||||||
const char *var_name,
|
|
||||||
StaState *sta);
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
Warning: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11.
|
Warning: gcd_sky130hd.v line 527, module sky130_fd_sc_hd__tapvpwrvgnd_1 not found. Creating black box for TAP_11.
|
||||||
Annotated 937 pin activities.
|
Annotated 937 pin activities.
|
||||||
|
vcd 937
|
||||||
|
unannotated 0
|
||||||
Group Internal Switching Leakage Total
|
Group Internal Switching Leakage Total
|
||||||
Power Power Power Power (Watts)
|
Power Power Power Power (Watts)
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -112,30 +112,30 @@ proc list_delete { list delete } {
|
||||||
|
|
||||||
# Record tests in sta/examples
|
# Record tests in sta/examples
|
||||||
record_example_tests {
|
record_example_tests {
|
||||||
sdf_delays
|
|
||||||
delay_calc
|
delay_calc
|
||||||
min_max_delays
|
min_max_delays
|
||||||
spef_parasitics
|
|
||||||
multi_corner
|
multi_corner
|
||||||
power
|
power
|
||||||
power_vcd
|
power_vcd
|
||||||
|
sdf_delays
|
||||||
|
spef_parasitics
|
||||||
}
|
}
|
||||||
|
|
||||||
record_sta_tests {
|
record_sta_tests {
|
||||||
prima3
|
|
||||||
verilog_attribute
|
|
||||||
liberty_arcs_one2one_1
|
|
||||||
liberty_arcs_one2one_2
|
|
||||||
get_is_memory
|
|
||||||
get_filter
|
get_filter
|
||||||
|
get_is_memory
|
||||||
|
get_lib_pins_of_objects
|
||||||
get_noargs
|
get_noargs
|
||||||
get_objrefs
|
get_objrefs
|
||||||
get_lib_pins_of_objects
|
liberty_arcs_one2one_1
|
||||||
|
liberty_arcs_one2one_2
|
||||||
|
liberty_ccsn
|
||||||
|
liberty_latch3
|
||||||
|
prima3
|
||||||
report_checks_src_attr
|
report_checks_src_attr
|
||||||
report_json1
|
report_json1
|
||||||
report_json2
|
report_json2
|
||||||
liberty_latch3
|
verilog_attribute
|
||||||
liberty_ccsn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
define_test_group fast [group_tests all]
|
define_test_group fast [group_tests all]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue