From 90720645e8917fe5c97f2584ff7351ef4f3d02dd Mon Sep 17 00:00:00 2001 From: James Cherry Date: Tue, 16 Aug 2022 22:43:42 -0700 Subject: [PATCH] read_vcd Signed-off-by: James Cherry --- CMakeLists.txt | 1 + search/ReadVcd.cc | 371 ++++++++++++++++++++++++++++++++++++++++++++++ search/ReadVcd.hh | 24 +++ tcl/StaTcl.i | 7 + 4 files changed, 403 insertions(+) create mode 100644 search/ReadVcd.cc create mode 100644 search/ReadVcd.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e47f123..e36cc3e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -172,6 +172,7 @@ set(STA_SOURCE search/PathRef.cc search/PathVertex.cc search/PathVertexRep.cc + search/ReadVcd.cc search/Power.cc search/Property.cc search/ReportPath.cc diff --git a/search/ReadVcd.cc b/search/ReadVcd.cc new file mode 100644 index 00000000..6a613899 --- /dev/null +++ b/search/ReadVcd.cc @@ -0,0 +1,371 @@ +// 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 . + +#include +#include +#include +#include + +#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 readStmtTokens(); + + + gzFile stream_; + string token_; + + string date_; + string comment_; + string version_; + double time_scale_; + string time_unit_; + double time_unit_scale_; + vector vars_; + map 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 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 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 +VcdReader::readStmtTokens() +{ + vector 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) +{ +} + +} diff --git a/search/ReadVcd.hh b/search/ReadVcd.hh new file mode 100644 index 00000000..1eb7ccbe --- /dev/null +++ b/search/ReadVcd.hh @@ -0,0 +1,24 @@ +// 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 . + +#pragma once + +namespace sta { + +void +readVcdFile(const char *filename); + +} diff --git a/tcl/StaTcl.i b/tcl/StaTcl.i index bdcc4ecf..f5f7888a 100644 --- a/tcl/StaTcl.i +++ b/tcl/StaTcl.i @@ -80,6 +80,7 @@ #include "search/Levelize.hh" #include "search/ReportPath.hh" #include "search/Power.hh" +#include "search/ReadVcd.hh" namespace sta { @@ -5569,6 +5570,12 @@ endpoint_count() return Sta::sta()->endpointCount(); } +void +read_vcd(const char *filename) +{ + readVcdFile(filename); +} + %} // inline ////////////////////////////////////////////////////////////////