372 lines
7.8 KiB
C++
372 lines
7.8 KiB
C++
// OpenSTA, Static Timing Analyzer
|
|
// Copyright (c) 2022, 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 <string>
|
|
#include <cctype>
|
|
#include <vector>
|
|
#include <map>
|
|
|
|
#include "Zlib.hh"
|
|
#include "ReadVcd.hh"
|
|
|
|
namespace sta {
|
|
|
|
using std::string;
|
|
using std::isspace;
|
|
using std::vector;
|
|
using std::map;
|
|
|
|
class VcdVar;
|
|
class VcdValue;
|
|
|
|
class VcdReader
|
|
{
|
|
public:
|
|
VcdReader();
|
|
void read(const char *filename);
|
|
void reportWaveforms();
|
|
|
|
private:
|
|
void parseTimescale();
|
|
void parseVar();
|
|
void parseScope();
|
|
void parseUpscope();
|
|
void parseDumpvars();
|
|
void parseVarValues();
|
|
void makeVarIdMap();
|
|
void appendVarValue(string id,
|
|
char value);
|
|
string getToken();
|
|
string readStmtString();
|
|
vector<string> readStmtTokens();
|
|
|
|
|
|
gzFile stream_;
|
|
string token_;
|
|
|
|
string date_;
|
|
string comment_;
|
|
string version_;
|
|
double time_scale_;
|
|
string time_unit_;
|
|
double time_unit_scale_;
|
|
vector<VcdVar> vars_;
|
|
map<string, VcdVar*> id_var_map_;
|
|
int64_t time_;
|
|
};
|
|
|
|
class VcdVar
|
|
{
|
|
public:
|
|
enum VarType {wire, reg};
|
|
VcdVar(string name,
|
|
VarType type,
|
|
int width,
|
|
string id);
|
|
const string& name() const { return name_; }
|
|
VarType type() const { return type_; }
|
|
int width() const { return width_; }
|
|
const string& id() const { return id_; }
|
|
void pushValue(int64_t time,
|
|
char value);
|
|
void pushBusValue(int64_t time,
|
|
int64_t bus_value);
|
|
|
|
private:
|
|
string name_;
|
|
VarType type_;
|
|
int width_;
|
|
string id_;
|
|
vector<VcdValue> values_;
|
|
};
|
|
|
|
class VcdValue
|
|
{
|
|
public:
|
|
VcdValue(uint64_t time,
|
|
char value,
|
|
uint64_t bus_value);
|
|
uint64_t time() const { return time_; }
|
|
char value() const { return value_; }
|
|
uint64_t busValue() const { return bus_value_; }
|
|
|
|
private:
|
|
uint64_t time_;
|
|
// 01XUZ or '\0' when width > 1 to use bus_value_.
|
|
char value_;
|
|
uint64_t bus_value_;
|
|
};
|
|
|
|
void
|
|
readVcdFile(const char *filename)
|
|
|
|
{
|
|
VcdReader reader;
|
|
reader.read(filename);
|
|
reader.reportWaveforms();
|
|
}
|
|
|
|
void
|
|
VcdReader::read(const char *filename)
|
|
{
|
|
stream_ = gzopen(filename, "r");
|
|
if (stream_) {
|
|
string token = getToken();
|
|
while (!token.empty()) {
|
|
if (token == "$date")
|
|
date_ = readStmtString();
|
|
if (token == "$comment")
|
|
comment_ = readStmtString();
|
|
else if (token == "$version")
|
|
version_ = readStmtString();
|
|
else if (token == "$timescale")
|
|
parseTimescale();
|
|
if (token == "$var")
|
|
parseVar();
|
|
else if (token == "$scope")
|
|
parseScope();
|
|
else if (token == "$upscope")
|
|
parseUpscope();
|
|
else if (token == "$dumpvars")
|
|
parseDumpvars();
|
|
else if (token == "$enddefinitions") {
|
|
// empty body
|
|
readStmtString();
|
|
makeVarIdMap();
|
|
parseVarValues();
|
|
}
|
|
token = getToken();
|
|
|
|
}
|
|
gzclose(stream_);
|
|
}
|
|
}
|
|
|
|
VcdReader::VcdReader() :
|
|
time_(0)
|
|
{
|
|
}
|
|
|
|
void
|
|
VcdReader::parseTimescale()
|
|
{
|
|
string timescale = readStmtString();
|
|
size_t last;
|
|
time_scale_ = std::stod(timescale, &last);
|
|
if (last == token_.size())
|
|
printf("Missing timescale units\n");
|
|
time_unit_ = timescale.substr(last + 1);
|
|
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
|
|
printf("unknown timescale unit\n");
|
|
}
|
|
|
|
void
|
|
VcdReader::parseVar()
|
|
{
|
|
vector<string> tokens = readStmtTokens();
|
|
if (tokens.size() != 4)
|
|
printf("$var syntax error\n");
|
|
else {
|
|
string type_name = tokens[0];
|
|
VcdVar::VarType type = VcdVar::wire;
|
|
if (type_name == "wire")
|
|
type = VcdVar::wire;
|
|
else if (type_name == "reg")
|
|
type = VcdVar::reg;
|
|
else
|
|
printf("$var unknown variable type\n");
|
|
|
|
int width = stoi(tokens[1]);
|
|
string id = tokens[2];
|
|
string name = tokens[3];
|
|
vars_.push_back(VcdVar(name, type, width, id));
|
|
}
|
|
}
|
|
|
|
void
|
|
VcdReader::parseScope()
|
|
{
|
|
}
|
|
|
|
void
|
|
VcdReader::parseUpscope()
|
|
{
|
|
}
|
|
|
|
void
|
|
VcdReader::parseDumpvars()
|
|
{
|
|
}
|
|
|
|
// vars_ grows so the map has to be built after the vars.
|
|
void
|
|
VcdReader::makeVarIdMap()
|
|
{
|
|
for (VcdVar &var : vars_)
|
|
id_var_map_[var.id()] = &var;
|
|
}
|
|
|
|
void
|
|
VcdReader::parseVarValues()
|
|
{
|
|
string token = getToken();
|
|
while (!token.empty()) {
|
|
if (token[0] == '#') {
|
|
time_ = stoll(token.substr(1));
|
|
printf("time = %lld\n", time_);
|
|
}
|
|
else if (token[0] == '0'
|
|
|| token[0] == '1'
|
|
|| token[0] == 'X'
|
|
|| token[0] == 'U'
|
|
|| token[0] == 'Z')
|
|
appendVarValue(token.substr(1), token[0]);
|
|
else if (token[0] == 'b') {
|
|
if (token[1] == 'X'
|
|
|| token[1] == 'U'
|
|
|| token[1] == 'Z') {
|
|
string id = getToken();
|
|
// Mixed 0/1/X/U not supported.
|
|
appendVarValue(id, token[1]);
|
|
}
|
|
else {
|
|
int64_t bus_value = stoll(token.substr(1));
|
|
string id = getToken();
|
|
auto var_itr = id_var_map_.find(id);
|
|
if (var_itr == id_var_map_.end())
|
|
printf("unknown var %s\n", id.c_str());
|
|
VcdVar *var = var_itr->second;
|
|
printf("%s = %lld\n", var->name().c_str(), bus_value);
|
|
var->pushBusValue(time_, bus_value);
|
|
}
|
|
}
|
|
token = getToken();
|
|
}
|
|
}
|
|
|
|
void
|
|
VcdReader::appendVarValue(string id,
|
|
char value)
|
|
{
|
|
auto var_itr = id_var_map_.find(id);
|
|
if (var_itr == id_var_map_.end())
|
|
printf("unknown var %s\n", id.c_str());
|
|
VcdVar *var = var_itr->second;
|
|
printf("%s = %c\n", var->name().c_str(), value);
|
|
var->pushValue(time_, value);
|
|
}
|
|
|
|
string
|
|
VcdReader::readStmtString()
|
|
{
|
|
string line;
|
|
string token = getToken();
|
|
while (!token.empty() && token != "$end") {
|
|
if (!line.empty())
|
|
line += " ";
|
|
line += token;
|
|
token = getToken();
|
|
}
|
|
return line;
|
|
}
|
|
|
|
vector<string>
|
|
VcdReader::readStmtTokens()
|
|
{
|
|
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_);
|
|
// skip whitespace
|
|
while (ch != EOF && isspace(ch))
|
|
ch = gzgetc(stream_);
|
|
while (ch != EOF && !isspace(ch)) {
|
|
token.push_back(ch);
|
|
ch = gzgetc(stream_);
|
|
}
|
|
if (ch == EOF)
|
|
return "";
|
|
else
|
|
return token;
|
|
}
|
|
|
|
void
|
|
VcdReader::reportWaveforms()
|
|
{
|
|
printf("Date: %s\n", date_.c_str());
|
|
printf("Timescale: %.2f%s\n", time_scale_, time_unit_.c_str());
|
|
for (VcdVar &var : vars_) {
|
|
printf(" %s %d %s\n",
|
|
var.name().c_str(),
|
|
var.width(),
|
|
var.id().c_str());
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
VcdVar::VcdVar(string name,
|
|
VarType type,
|
|
int width,
|
|
string id) :
|
|
name_(name),
|
|
type_(type),
|
|
width_(width),
|
|
id_(id)
|
|
{
|
|
}
|
|
|
|
void
|
|
VcdVar::pushValue(int64_t time,
|
|
char value)
|
|
{
|
|
values_.push_back(VcdValue(time, value, 0));
|
|
}
|
|
|
|
void
|
|
VcdVar::pushBusValue(int64_t time,
|
|
int64_t bus_value)
|
|
{
|
|
values_.push_back(VcdValue(time, '\0', bus_value));
|
|
}
|
|
|
|
VcdValue::VcdValue(uint64_t time,
|
|
char value,
|
|
uint64_t bus_value) :
|
|
time_(time),
|
|
value_(value),
|
|
bus_value_(bus_value)
|
|
{
|
|
}
|
|
|
|
}
|