2022-10-28 00:12:32 +02:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2024-01-12 01:34:49 +01:00
|
|
|
// Copyright (c) 2024, Parallax Software, Inc.
|
2022-10-28 00:12:32 +02:00
|
|
|
//
|
|
|
|
|
// 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"
|
|
|
|
|
|
2023-11-24 18:57:27 +01:00
|
|
|
#include <inttypes.h>
|
2024-09-24 03:04:26 +02:00
|
|
|
#include <set>
|
2023-11-24 18:57:27 +01:00
|
|
|
|
2022-10-28 00:12:32 +02:00
|
|
|
#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 {
|
|
|
|
|
|
2024-07-29 17:55:02 +02:00
|
|
|
using std::abs;
|
2022-10-28 00:12:32 +02:00
|
|
|
using std::min;
|
|
|
|
|
using std::to_string;
|
|
|
|
|
|
2022-10-30 21:49:38 +01:00
|
|
|
class ReadVcdActivities : public StaState
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ReadVcdActivities(const char *filename,
|
|
|
|
|
const char *scope,
|
|
|
|
|
Sta *sta);
|
|
|
|
|
void readActivities();
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void setActivities();
|
2023-01-07 02:47:38 +01:00
|
|
|
void setVarActivity(VcdVar *var,
|
|
|
|
|
string &var_name,
|
|
|
|
|
const VcdValues &var_value);
|
2022-11-02 03:08:22 +01:00
|
|
|
void setVarActivity(const char *pin_name,
|
|
|
|
|
const VcdValues &var_values,
|
|
|
|
|
int value_bit);
|
|
|
|
|
void findVarActivity(const VcdValues &var_values,
|
2022-10-30 21:49:38 +01:00
|
|
|
int value_bit,
|
2022-11-02 03:08:22 +01:00
|
|
|
// Return values.
|
2022-11-03 02:17:04 +01:00
|
|
|
double &transition_count,
|
|
|
|
|
double &activity,
|
|
|
|
|
double &duty);
|
2022-11-02 03:08:22 +01:00
|
|
|
void checkClkPeriod(const Pin *pin,
|
2022-11-03 02:17:04 +01:00
|
|
|
double transition_count);
|
2022-10-30 21:49:38 +01:00
|
|
|
|
|
|
|
|
const char *filename_;
|
|
|
|
|
const char *scope_;
|
|
|
|
|
Vcd vcd_;
|
2022-11-03 02:17:04 +01:00
|
|
|
double clk_period_;
|
2022-10-30 21:49:38 +01:00
|
|
|
Sta *sta_;
|
|
|
|
|
Power *power_;
|
2024-09-24 03:04:26 +02:00
|
|
|
std::set<const Pin*> annotated_pins_;
|
2022-11-03 02:17:04 +01:00
|
|
|
|
|
|
|
|
static constexpr double sim_clk_period_tolerance_ = .1;
|
2022-10-30 21:49:38 +01:00
|
|
|
};
|
2022-10-30 18:17:40 +01:00
|
|
|
|
2022-10-28 00:12:32 +02:00
|
|
|
void
|
|
|
|
|
readVcdActivities(const char *filename,
|
2022-10-30 21:26:16 +01:00
|
|
|
const char *scope,
|
2022-10-28 00:12:32 +02:00
|
|
|
Sta *sta)
|
|
|
|
|
{
|
2022-10-30 21:49:38 +01:00
|
|
|
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),
|
2022-11-04 17:33:03 +01:00
|
|
|
clk_period_(0.0),
|
2022-10-30 21:49:38 +01:00
|
|
|
sta_(sta),
|
|
|
|
|
power_(sta->power())
|
|
|
|
|
{
|
2022-10-28 00:12:32 +02:00
|
|
|
}
|
|
|
|
|
|
2022-10-30 21:49:38 +01:00
|
|
|
void
|
|
|
|
|
ReadVcdActivities::readActivities()
|
2022-10-28 00:12:32 +02:00
|
|
|
{
|
2022-10-30 21:49:38 +01:00
|
|
|
vcd_ = readVcdFile(filename_, sta_);
|
|
|
|
|
|
|
|
|
|
clk_period_ = INF;
|
|
|
|
|
for (Clock *clk : *sta_->sdc()->clocks())
|
2022-11-03 02:17:04 +01:00
|
|
|
clk_period_ = min(static_cast<double>(clk->period()), clk_period_);
|
2022-10-28 00:12:32 +02:00
|
|
|
|
2023-10-18 20:14:25 +02:00
|
|
|
if (vcd_.timeMax() > 0)
|
|
|
|
|
setActivities();
|
|
|
|
|
else
|
2024-01-08 03:23:53 +01:00
|
|
|
report_->warn(1450, "VCD max time is zero.");
|
2024-06-27 22:57:58 +02:00
|
|
|
report_->reportLine("Annotated %zu pin activities.", annotated_pins_.size());
|
2022-10-30 21:49:38 +01:00
|
|
|
}
|
2022-10-28 00:12:32 +02:00
|
|
|
|
2022-10-30 21:49:38 +01:00
|
|
|
void
|
|
|
|
|
ReadVcdActivities::setActivities()
|
|
|
|
|
{
|
|
|
|
|
size_t scope_length = strlen(scope_);
|
2022-11-02 03:08:22 +01:00
|
|
|
for (VcdVar *var : vcd_.vars()) {
|
2022-10-30 21:49:38 +01:00
|
|
|
const VcdValues &var_values = vcd_.values(var);
|
2022-10-30 21:26:16 +01:00
|
|
|
if (!var_values.empty()
|
2022-11-02 03:08:22 +01:00
|
|
|
&& (var->type() == VcdVarType::wire
|
|
|
|
|
|| var->type() == VcdVarType::reg)) {
|
|
|
|
|
string var_name = var->name();
|
2022-10-30 21:26:16 +01:00
|
|
|
// string::starts_with in c++20
|
2023-01-07 02:47:38 +01:00
|
|
|
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);
|
2022-10-28 00:12:32 +02:00
|
|
|
}
|
2023-01-07 02:47:38 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
setVarActivity(var, var_name, var_values);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReadVcdActivities::setVarActivity(VcdVar *var,
|
|
|
|
|
string &var_name,
|
|
|
|
|
const VcdValues &var_values)
|
|
|
|
|
{
|
2023-10-17 05:44:39 +02:00
|
|
|
if (var->width() == 1) {
|
|
|
|
|
string sta_name = netVerilogToSta(var_name.c_str());
|
2023-03-26 15:34:36 +02:00
|
|
|
setVarActivity(sta_name.c_str(), var_values, 0);
|
2023-10-17 05:44:39 +02:00
|
|
|
}
|
2023-01-07 02:47:38 +01:00
|
|
|
else {
|
2023-06-04 18:34:29 +02:00
|
|
|
bool is_bus, is_range, subscript_wild;
|
2023-03-26 15:34:36 +02:00
|
|
|
string bus_name;
|
2023-01-07 02:47:38 +01:00
|
|
|
int from, to;
|
2023-10-17 05:44:39 +02:00
|
|
|
parseBusName(var_name.c_str(), '[', ']', '\\',
|
2023-06-04 18:34:29 +02:00
|
|
|
is_bus, is_range, bus_name, from, to, subscript_wild);
|
2023-10-17 05:44:39 +02:00
|
|
|
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++;
|
|
|
|
|
}
|
2022-10-28 00:12:32 +02:00
|
|
|
}
|
2023-10-17 05:44:39 +02:00
|
|
|
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++;
|
|
|
|
|
}
|
2023-01-07 02:47:38 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-10-17 05:44:39 +02:00
|
|
|
else
|
2024-01-08 03:23:53 +01:00
|
|
|
report_->warn(1451, "problem parsing bus %s.", var_name.c_str());
|
2022-10-28 00:12:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-30 21:49:38 +01:00
|
|
|
void
|
2022-11-02 03:08:22 +01:00
|
|
|
ReadVcdActivities::setVarActivity(const char *pin_name,
|
|
|
|
|
const VcdValues &var_values,
|
|
|
|
|
int value_bit)
|
|
|
|
|
{
|
2023-10-20 04:40:21 +02:00
|
|
|
const Pin *pin = sdc_network_->findPin(pin_name);
|
2022-11-02 03:08:22 +01:00
|
|
|
if (pin) {
|
2023-10-23 01:51:18 +02:00
|
|
|
debugPrint(debug_, "read_vcd_activities", 3, "%s values", pin_name);
|
2022-11-03 02:17:04 +01:00
|
|
|
double transition_count, activity, duty;
|
2022-11-02 03:08:22 +01:00
|
|
|
findVarActivity(var_values, value_bit,
|
|
|
|
|
transition_count, activity, duty);
|
|
|
|
|
debugPrint(debug_, "read_vcd_activities", 1,
|
2022-11-03 02:17:04 +01:00
|
|
|
"%s transitions %.1f activity %.2f duty %.2f",
|
2022-11-02 03:08:22 +01:00
|
|
|
pin_name,
|
|
|
|
|
transition_count,
|
|
|
|
|
activity,
|
|
|
|
|
duty);
|
|
|
|
|
if (sdc_->isLeafPinClock(pin))
|
|
|
|
|
checkClkPeriod(pin, transition_count);
|
2023-10-22 02:16:39 +02:00
|
|
|
power_->setUserActivity(pin, activity, duty, PwrActivityOrigin::vcd);
|
2023-10-22 02:08:58 +02:00
|
|
|
annotated_pins_.insert(pin);
|
2022-11-02 03:08:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ReadVcdActivities::findVarActivity(const VcdValues &var_values,
|
2022-10-30 21:49:38 +01:00
|
|
|
int value_bit,
|
2022-11-02 03:08:22 +01:00
|
|
|
// Return values.
|
2022-11-03 02:17:04 +01:00
|
|
|
double &transition_count,
|
|
|
|
|
double &activity,
|
|
|
|
|
double &duty)
|
2022-10-28 00:12:32 +02:00
|
|
|
{
|
2022-11-03 02:17:04 +01:00
|
|
|
transition_count = 0.0;
|
2023-10-23 01:51:18 +02:00
|
|
|
char prev_value = var_values[0].value(value_bit);
|
2022-10-30 18:17:40 +01:00
|
|
|
VcdTime prev_time = var_values[0].time();
|
|
|
|
|
VcdTime high_time = 0;
|
|
|
|
|
for (const VcdValue &var_value : var_values) {
|
|
|
|
|
VcdTime time = var_value.time();
|
2023-10-23 01:51:18 +02:00
|
|
|
char value = var_value.value(value_bit);
|
2023-11-24 18:57:27 +01:00
|
|
|
debugPrint(debug_, "read_vcd_activities", 3, " %" PRId64 " %c", time, value);
|
2022-10-30 18:17:40 +01:00
|
|
|
if (prev_value == '1')
|
|
|
|
|
high_time += time - prev_time;
|
2022-11-02 16:32:42 +01:00
|
|
|
if (value != prev_value)
|
2022-11-03 02:17:04 +01:00
|
|
|
transition_count += (value == 'X'
|
|
|
|
|
|| value == 'Z'
|
|
|
|
|
|| prev_value == 'X'
|
|
|
|
|
|| prev_value == 'Z')
|
|
|
|
|
? .5
|
|
|
|
|
: 1.0;
|
2022-10-30 18:17:40 +01:00
|
|
|
prev_time = time;
|
|
|
|
|
prev_value = value;
|
|
|
|
|
}
|
2022-10-30 21:49:38 +01:00
|
|
|
VcdTime time_max = vcd_.timeMax();
|
2022-10-30 18:17:40 +01:00
|
|
|
if (prev_value == '1')
|
|
|
|
|
high_time += time_max - prev_time;
|
2022-11-03 02:17:04 +01:00
|
|
|
duty = static_cast<double>(high_time) / time_max;
|
2024-03-28 19:03:14 +01:00
|
|
|
activity = transition_count / (time_max * vcd_.timeScale() / clk_period_);
|
2022-11-02 03:08:22 +01:00
|
|
|
}
|
2022-10-30 18:17:40 +01:00
|
|
|
|
2022-11-02 03:08:22 +01:00
|
|
|
void
|
|
|
|
|
ReadVcdActivities::checkClkPeriod(const Pin *pin,
|
2022-11-03 02:17:04 +01:00
|
|
|
double transition_count)
|
2022-11-02 03:08:22 +01:00
|
|
|
{
|
|
|
|
|
VcdTime time_max = vcd_.timeMax();
|
2024-03-28 19:03:14 +01:00
|
|
|
double sim_period = time_max * vcd_.timeScale() / (transition_count / 2.0);
|
2022-11-02 03:08:22 +01:00
|
|
|
|
|
|
|
|
ClockSet *clks = sdc_->findLeafPinClocks(pin);
|
|
|
|
|
if (clks) {
|
|
|
|
|
for (Clock *clk : *clks) {
|
2022-11-03 02:17:04 +01:00
|
|
|
double clk_period = clk->period();
|
|
|
|
|
if (abs((clk_period - sim_period) / clk_period) > .1)
|
|
|
|
|
// Warn if sim clock period differs from SDC by 10%.
|
2024-01-08 03:23:53 +01:00
|
|
|
report_->warn(1452, "clock %s vcd period %s differs from SDC clock period %s",
|
2022-11-03 02:17:04 +01:00
|
|
|
clk->name(),
|
|
|
|
|
delayAsString(sim_period, this),
|
|
|
|
|
delayAsString(clk_period, this));
|
2022-11-02 03:08:22 +01:00
|
|
|
}
|
2022-10-28 00:12:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|