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