blob: ff1879203fcbcf19e5a5e296fbfa4959d6f551dc [file] [log] [blame]
/*
* Copyright 2018 Google, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "systemc/utils/vcd.hh"
#include <ctime>
#include <iomanip>
#include "base/bitfield.hh"
#include "base/cprintf.hh"
#include "sim/core.hh"
#include "systemc/core/scheduler.hh"
#include "systemc/ext/core/sc_event.hh"
#include "systemc/ext/core/sc_main.hh"
#include "systemc/ext/core/sc_time.hh"
#include "systemc/ext/dt/bit/sc_bv_base.hh"
#include "systemc/ext/dt/bit/sc_logic.hh"
#include "systemc/ext/dt/bit/sc_lv_base.hh"
#include "systemc/ext/dt/fx/sc_fxnum.hh"
#include "systemc/ext/dt/fx/sc_fxval.hh"
#include "systemc/ext/dt/int/sc_int_base.hh"
#include "systemc/ext/dt/int/sc_signed.hh"
#include "systemc/ext/dt/int/sc_uint_base.hh"
#include "systemc/ext/dt/int/sc_unsigned.hh"
#include "systemc/ext/utils/functions.hh"
namespace sc_gem5
{
namespace
{
std::string
cleanName(std::string name)
{
for (int i = 0; i < name.length(); i++) {
if (name[i] == '[')
name[i] = '(';
else if (name[i] == ']')
name[i] = ')';
}
return name;
}
} // anonymous namespace
class VcdTraceValBase : public TraceValBase
{
protected:
std::string _vcdName;
const char *
stripLeadingBits(const char *orig)
{
const char first = orig[0];
if (first != 'z' && first != 'x' && first != '0')
return orig;
const char *res = orig;
while (*++res == first) {}
if (first != '0' || *res != '1')
res--;
return res;
}
char
scLogicToVcdState(char in)
{
switch (in) {
case 'U':
case 'X':
case 'W':
case 'D':
return 'x';
case '0':
case 'L':
return '0';
case '1':
case 'H':
return '1';
case 'Z':
return 'z';
default:
return '?';
}
}
void
printVal(std::ostream &os, const std::string &rep)
{
switch (width()) {
case 0:
return;
case 1:
os << rep << vcdName() << std::endl;;
return;
default:
os << "b" << stripLeadingBits(rep.c_str()) << " " <<
vcdName() << std::endl;
return;
}
}
public:
VcdTraceValBase(int width) : TraceValBase(width) {}
~VcdTraceValBase() {}
void vcdName(const std::string &vcd_name) { _vcdName = vcd_name; }
const std::string &vcdName() { return _vcdName; }
virtual std::string vcdType() { return "wire"; }
virtual void output(std::ostream &os) = 0;
};
void
VcdTraceScope::addValue(const std::string &name, VcdTraceValBase *value)
{
size_t pos = name.find_first_of('.');
if (pos == std::string::npos) {
values.emplace_back(name, value);
} else {
std::string sname = name.substr(0, pos);
auto it = scopes.find(sname);
if (it == scopes.end())
it = scopes.emplace(sname, new VcdTraceScope).first;
it->second->addValue(name.substr(pos + 1), value);
}
}
void
VcdTraceScope::output(const std::string &name, std::ostream &os)
{
os << "$scope module " << name << " $end" << std::endl;
for (auto &p: values) {
const std::string &name = p.first;
VcdTraceValBase *value = p.second;
int w = value->width();
if (w <= 0) {
std::string msg = gem5::csprintf("'%s' has 0 bits", name);
// The typo in this error message is intentional to match the
// Accellera output.
SC_REPORT_ERROR("(E710) object cannot not be traced", msg.c_str());
return;
}
std::string clean_name = cleanName(name);
if (w == 1) {
gem5::ccprintf(os, "$var %s % 3d %s %s $end\n",
value->vcdType(), w, value->vcdName(), clean_name);
} else {
gem5::ccprintf(os, "$var %s % 3d %s %s [%d:0] $end\n",
value->vcdType(), w, value->vcdName(), clean_name, w - 1);
}
}
for (auto &p: scopes)
p.second->output(p.first, os);
os << "$upscope $end" << std::endl;
}
template <typename T>
class VcdTraceVal : public TraceVal<T, VcdTraceValBase>
{
public:
typedef T TracedType;
VcdTraceVal(const T* t, const std::string &vcd_name, int width) :
TraceVal<T, VcdTraceValBase>(t, width)
{
this->vcdName(vcd_name);
}
};
std::string
VcdTraceFile::nextSignalName()
{
std::string name(_nextName);
bool carry = false;
int pos = NextNameChars - 1;
do {
carry = (_nextName[pos] == 'z');
if (carry)
_nextName[pos--] = 'a';
else
_nextName[pos--]++;
} while (carry && pos >= 0);
return name;
}
void
VcdTraceFile::initialize()
{
finalizeTime();
// Date.
stream() << "$date" << std::endl;
time_t long_time;
time(&long_time);
struct tm *p_tm = localtime(&long_time);
stream() << std::put_time(p_tm, " %b %d, %Y %H:%M:%S\n");
stream() << "$end" << std::endl << std::endl;
// Version.
stream() << "$version" << std::endl;
stream() << " " << ::sc_core::sc_version() << std::endl;
stream() << "$end" << std::endl << std::endl;
// Timescale.
stream() << "$timescale" << std::endl;
stream() << " " << ::sc_core::sc_time::from_value(timeUnitTicks) <<
std::endl;
stream() << "$end" << std::endl << std::endl;
for (auto tv: traceVals)
tv->finalize();
topScope.output("SystemC", stream());
stream() << "$enddefinitions $end" << std::endl << std::endl;
gem5::Tick now = scheduler.getCurTick();
std::string timedump_comment =
gem5::csprintf("All initial values are dumped below at time "
"%g sec = %g timescale units.",
static_cast<double>(now) / gem5::sim_clock::as_float::s,
static_cast<double>(now / timeUnitTicks));
writeComment(timedump_comment);
lastPrintedTime = now / timeUnitTicks;
stream() << "$dumpvars" << std::endl;
for (auto tv: traceVals)
tv->output(stream());
stream() << "$end" << std::endl << std::endl;
initialized = true;
}
VcdTraceFile::~VcdTraceFile()
{
for (auto tv: traceVals)
delete tv;
traceVals.clear();
if (timeUnitTicks) {
gem5::ccprintf(stream(), "#%u\n",
scheduler.getCurTick() / timeUnitTicks);
}
}
void
VcdTraceFile::trace(bool delta)
{
if (!delta)
deltasAtNow = 0;
uint64_t deltaOffset = deltasAtNow;
if (delta)
deltaOffset = deltasAtNow++;
if (_traceDeltas != delta)
return;
if (!initialized) {
initialize();
return;
}
gem5::Tick now = scheduler.getCurTick() / timeUnitTicks + deltaOffset;
if (now <= lastPrintedTime) {
// TODO warn about reversed time?
return;
}
bool time_printed = false;
for (auto tv: traceVals) {
if (tv->check()) {
if (!time_printed) {
lastPrintedTime = now;
gem5::ccprintf(stream(), "#%u\n", now);
time_printed = true;
}
tv->output(stream());
}
}
if (time_printed)
stream() << std::endl;
}
class VcdTraceValBool : public VcdTraceVal<bool>
{
public:
using VcdTraceVal<bool>::VcdTraceVal;
void
output(std::ostream &os) override
{
printVal(os, this->value() ? "1" : "0");
}
};
void
VcdTraceFile::addTraceVal(const bool *v, const std::string &name)
{
addNewTraceVal<VcdTraceValBool>(v, name);
}
template <typename T>
class VcdTraceValFloat : public VcdTraceVal<T>
{
public:
using VcdTraceVal<T>::VcdTraceVal;
std::string vcdType() override { return "real"; }
void
output(std::ostream &os) override
{
gem5::ccprintf(os, "r%.16g %s\n", this->value(), this->vcdName());
}
};
void
VcdTraceFile::addTraceVal(const float *v, const std::string &name)
{
addNewTraceVal<VcdTraceValFloat<float>>(v, name);
}
void
VcdTraceFile::addTraceVal(const double *v, const std::string &name)
{
addNewTraceVal<VcdTraceValFloat<double>>(v, name);
}
class VcdTraceValScLogic : public VcdTraceVal<sc_dt::sc_logic>
{
public:
using VcdTraceVal<sc_dt::sc_logic>::VcdTraceVal;
void
output(std::ostream &os) override
{
char str[2] = {
scLogicToVcdState(value().to_char()),
'\0'
};
printVal(os, str);
}
};
void
VcdTraceFile::addTraceVal(const sc_dt::sc_logic *v, const std::string &name)
{
addNewTraceVal<VcdTraceValScLogic>(v, name);
}
template <typename T>
class VcdTraceValFinite : public VcdTraceVal<T>
{
public:
using VcdTraceVal<T>::VcdTraceVal;
void
finalize() override
{
VcdTraceVal<T>::finalize();
this->_width = this->value().length();
}
void
output(std::ostream &os) override
{
std::string str;
const int w = this->width();
str.reserve(w);
for (int i = w - 1; i >= 0; i--)
str += this->value()[i].to_bool() ? '1' : '0';
this->printVal(os, str);
}
};
void
VcdTraceFile::addTraceVal(const sc_dt::sc_int_base *v,
const std::string &name)
{
addNewTraceVal<VcdTraceValFinite<sc_dt::sc_int_base>>(v, name);
}
void
VcdTraceFile::addTraceVal(const sc_dt::sc_uint_base *v,
const std::string &name)
{
addNewTraceVal<VcdTraceValFinite<sc_dt::sc_uint_base>>(v, name);
}
void
VcdTraceFile::addTraceVal(const sc_dt::sc_signed *v, const std::string &name)
{
addNewTraceVal<VcdTraceValFinite<sc_dt::sc_signed>>(v, name);
}
void
VcdTraceFile::addTraceVal(const sc_dt::sc_unsigned *v,
const std::string &name)
{
addNewTraceVal<VcdTraceValFinite<sc_dt::sc_unsigned>>(v, name);
}
template <typename T>
class VcdTraceValLogic : public VcdTraceVal<T>
{
public:
using VcdTraceVal<T>::VcdTraceVal;
void
finalize() override
{
VcdTraceVal<T>::finalize();
this->_width = this->value().length();
}
void
output(std::ostream &os) override
{
this->printVal(os, this->value().to_string());
}
};
void
VcdTraceFile::addTraceVal(const sc_dt::sc_bv_base *v, const std::string &name)
{
addNewTraceVal<VcdTraceValLogic<::sc_dt::sc_bv_base>>(v, name);
}
void
VcdTraceFile::addTraceVal(const sc_dt::sc_lv_base *v, const std::string &name)
{
addNewTraceVal<VcdTraceValLogic<::sc_dt::sc_lv_base>>(v, name);
}
template <typename T>
class VcdTraceValFxval : public VcdTraceVal<T>
{
public:
using VcdTraceVal<T>::VcdTraceVal;
std::string vcdType() override { return "real"; }
void
output(std::ostream &os) override
{
gem5::ccprintf(os, "r%.16g %s\n",
this->value().to_double(), this->vcdName());
}
};
void
VcdTraceFile::addTraceVal(const sc_dt::sc_fxval *v, const std::string &name)
{
addNewTraceVal<VcdTraceValFxval<sc_dt::sc_fxval>>(v, name);
}
void
VcdTraceFile::addTraceVal(const sc_dt::sc_fxval_fast *v,
const std::string &name)
{
addNewTraceVal<VcdTraceValFxval<sc_dt::sc_fxval_fast>>(v, name);
}
template <typename T>
class VcdTraceValFxnum : public VcdTraceVal<T>
{
public:
using VcdTraceVal<T>::VcdTraceVal;
void
output(std::ostream &os) override
{
std::string str;
const int w = this->width();
str.reserve(w);
for (int i = w - 1; i >= 0; i--)
str += this->value()[i] ? '1' : '0';
this->printVal(os, str);
}
};
void
VcdTraceFile::addTraceVal(const sc_dt::sc_fxnum *v, const std::string &name)
{
addNewTraceVal<VcdTraceValFxnum<::sc_dt::sc_fxnum>>(v, name);
}
void
VcdTraceFile::addTraceVal(const sc_dt::sc_fxnum_fast *v,
const std::string &name)
{
addNewTraceVal<VcdTraceValFxnum<::sc_dt::sc_fxnum_fast>>(v, name);
}
class VcdTraceValEvent : public VcdTraceVal<::sc_core::sc_event>
{
public:
using VcdTraceVal<::sc_core::sc_event>::VcdTraceVal;
std::string vcdType() override { return "event"; }
void
output(std::ostream &os) override
{
if (value())
printVal(os, "1");
else
os << std::endl;
}
};
void
VcdTraceFile::addTraceVal(const sc_core::sc_event *v, const std::string &name)
{
addNewTraceVal<VcdTraceValEvent>(v, name);
}
class VcdTraceValTime : public VcdTraceVal<::sc_core::sc_time>
{
private:
static const int TimeWidth = 64;
public:
using VcdTraceVal<::sc_core::sc_time>::VcdTraceVal;
std::string vcdType() override { return "time"; }
void
finalize() override
{
VcdTraceVal<::sc_core::sc_time>::finalize();
_width = TimeWidth;
}
void
output(std::ostream &os) override
{
char str[TimeWidth + 1];
str[TimeWidth] = '\0';
const uint64_t val = value().value();
for (int i = 0; i < TimeWidth; i++)
str[i] = gem5::bits(val, TimeWidth - i - 1) ? '1' : '0';
printVal(os, str);
}
};
void
VcdTraceFile::addTraceVal(const sc_core::sc_time *v, const std::string &name)
{
addNewTraceVal<VcdTraceValTime>(v, name);
}
template <typename T>
class VcdTraceValInt : public VcdTraceVal<T>
{
public:
using VcdTraceVal<T>::VcdTraceVal;
void
output(std::ostream &os) override
{
const int w = this->width();
char str[w + 1];
str[w] = '\0';
const uint64_t val =
static_cast<uint64_t>(this->value()) & gem5::mask(sizeof(T) * 8);
if (gem5::mask(w) < val) {
for (int i = 0; i < w; i++)
str[i] = 'x';
} else {
for (int i = 0; i < w; i++)
str[i] = gem5::bits(val, w - i - 1) ? '1' : '0';
}
this->printVal(os, str);
}
};
void
VcdTraceFile::addTraceVal(const unsigned char *v, const std::string &name,
int width)
{
addNewTraceVal<VcdTraceValInt<unsigned char>>(v, name, width);
}
void
VcdTraceFile::addTraceVal(const char *v, const std::string &name, int width)
{
addNewTraceVal<VcdTraceValInt<char>>(v, name, width);
}
void
VcdTraceFile::addTraceVal(const unsigned short *v, const std::string &name,
int width)
{
addNewTraceVal<VcdTraceValInt<unsigned short>>(v, name, width);
}
void
VcdTraceFile::addTraceVal(const short *v, const std::string &name, int width)
{
addNewTraceVal<VcdTraceValInt<short>>(v, name, width);
}
void
VcdTraceFile::addTraceVal(const unsigned int *v, const std::string &name,
int width)
{
addNewTraceVal<VcdTraceValInt<unsigned int>>(v, name, width);
}
void
VcdTraceFile::addTraceVal(const int *v, const std::string &name, int width)
{
addNewTraceVal<VcdTraceValInt<int>>(v, name, width);
}
void
VcdTraceFile::addTraceVal(const unsigned long *v, const std::string &name,
int width)
{
addNewTraceVal<VcdTraceValInt<unsigned long>>(v, name, width);
}
void
VcdTraceFile::addTraceVal(const long *v, const std::string &name, int width)
{
addNewTraceVal<VcdTraceValInt<long>>(v, name, width);
}
void
VcdTraceFile::addTraceVal(const sc_dt::int64 *v, const std::string &name,
int width)
{
addNewTraceVal<VcdTraceValInt<sc_dt::int64>>(v, name, width);
}
void
VcdTraceFile::addTraceVal(const sc_dt::uint64 *v, const std::string &name,
int width)
{
addNewTraceVal<VcdTraceValInt<sc_dt::uint64>>(v, name, width);
}
void
VcdTraceFile::addTraceVal(const unsigned int *v, const std::string &name,
const char **literals)
{
uint64_t count = 0;
while (*literals++)
count++;
int bits = 0;
while (count >> bits)
bits++;
addNewTraceVal<VcdTraceValInt<unsigned int>>(v, name, bits);
}
void
VcdTraceFile::writeComment(const std::string &comment)
{
stream() << "$comment" << std::endl;
stream() << comment << std::endl;
stream() << "$end" << std::endl << std::endl;
}
} // namespace sc_gem5