Compare commits

...

14 Commits

Author SHA1 Message Date
Todd Strader
15a3f31d11 Preload data option 2020-01-24 15:44:41 -05:00
Todd Strader
4430edf6da Time replay 2020-01-22 18:05:34 -05:00
Todd Strader
505949ec54 Output checking 2020-01-22 06:39:38 -05:00
Todd Strader
4952fd5203 Cleanup 2020-01-21 18:52:54 -05:00
Todd Strader
2b93cfb364 Fix V3File dependency issue 2020-01-21 07:04:01 -05:00
Todd Strader
29b8b24b07 Signal list parsing 2020-01-21 05:24:50 -05:00
Todd Strader
7a6a590b64 Update note 2020-01-20 11:21:10 -05:00
Todd Strader
342356c65a Don't need the mock file anymore 2020-01-20 11:19:54 -05:00
Todd Strader
285ed67d83 Signs of life 2020-01-18 16:17:41 -05:00
Todd Strader
12716bd1e8 Connecting more dots 2020-01-18 16:17:41 -05:00
Todd Strader
d8b75fa352 Making progress? 2020-01-18 16:17:41 -05:00
Todd Strader
e4d75ed384 No replay yet, but produces an FST 2020-01-18 16:17:41 -05:00
Todd Strader
fdef131113 Working towards native Verilator replay without verilator_replay 2020-01-18 16:17:41 -05:00
Todd Strader
2c25c25379 WIP - Add verilator_replay utility 2020-01-18 16:17:41 -05:00
15 changed files with 1033 additions and 5 deletions

1
.gitignore vendored
View File

@ -29,6 +29,7 @@ internals.txt
verilator.txt
verilator_bin*
verilator_coverage_bin*
verilator_replay_bin*
verilator.pc
verilator-config.cmake
verilator-config-version.cmake

View File

@ -0,0 +1,304 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Copyright 2003-2020 by Todd Strader. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License.
// Version 2.0.
//
// Verilator 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.
//
//=========================================================================
///
/// \file
/// \brief Verilator: Common functions for replay tool
///
/// See verilator_replay
///
/// Code available from: http://www.veripool.org/verilator
///
//=========================================================================
#include "verilated_replay.h"
#include <cstring>
// TODO -- collapse into constructor?
int VerilatedReplay::init() {
createMod();
addSignals();
for (SignalNameMap::iterator it = m_inputNames.begin(); it != m_inputNames.end(); ++it) {
addInputName(it->first);
}
for (SignalNameMap::iterator it = m_outputNames.begin(); it != m_outputNames.end(); ++it) {
addOutputName(it->first);
}
openFst(m_fstName);
searchFst(NULL);
m_time = fstReaderGetStartTime(m_fstp);
m_preloadTime = m_time;
// TODO -- use FST timescale
m_simTime = m_time;
for (VarMap::iterator it = m_inputs.begin(); it != m_inputs.end();
++it) {
VL_PRINTF("input %s = %d\n", it->second.fullName.c_str(), it->first);
fstReaderSetFacProcessMask(m_fstp, it->first);
// TODO -- double check the size hasn't changed or just defer looking at size until here
m_inputHandles[it->first] = FstSignal(it->second.hier.u.var.length,
m_inputNames[it->second.fullName].signal);
}
for (VarMap::iterator it = m_outputs.begin(); it != m_outputs.end();
++it) {
VL_PRINTF("output %s = %d\n", it->second.fullName.c_str(), it->first);
fstReaderSetFacProcessMask(m_fstp, it->first);
size_t bits = it->second.hier.u.var.length;
size_t bytes = (bits + 7) / 8;
vluint8_t* buffer = new vluint8_t [bytes];
// TODO -- double check the size hasn't changed or just defer looking at size until here
m_outputHandles[it->first] = FstSignal(bits, m_outputNames[it->second.fullName].signal,
buffer);
}
if (m_preloadData) {
// TODO -- should I be using fstReaderIterBlocks instead? (only one CB)
// It appears that 0 is the error return code
if (fstReaderIterBlocks2(m_fstp, &VerilatedReplay::fstCallback,
&VerilatedReplay::fstCallbackVarlen, this, NULL) == 0) {
VL_PRINTF("Error iterating FST\n");
exit(-1);
}
// Add final time
addPreloadTime();
}
return 0;
}
VerilatedReplay::~VerilatedReplay() {
fstReaderClose(m_fstp);
for (SignalHandleMap::iterator it = m_outputHandles.begin(); it != m_outputHandles.end(); ++it) {
delete [] it->second.expected;
}
for (ReplayVector::iterator it = m_replayData.begin(); it != m_replayData.end(); ++it) {
if (it->type != ReplayData::ReplayType::Time) delete [] it->data;
}
#if VM_TRACE
if (m_tfp) m_tfp->close();
#endif
delete m_modp;
}
void VerilatedReplay::addInput(const std::string& fullName, vluint8_t* signal, size_t size) {
m_inputNames[fullName] = FstSignal(size, signal);
}
void VerilatedReplay::addOutput(const std::string& fullName, vluint8_t* signal, size_t size) {
m_outputNames[fullName] = FstSignal(size, signal);
}
void VerilatedReplay::replayPreloadedData() {
for (ReplayVector::iterator it = m_replayData.begin(); it != m_replayData.end(); ++it) {
switch (it->type) {
case ReplayData::ReplayType::Input:
case ReplayData::ReplayType::Output:
memcpy(it->u.target, it->data, it->size);
break;
case ReplayData::ReplayType::Time:
m_time = it->u.time;
// TODO -- use FST timescale
m_simTime = m_time;
eval();
break;
}
}
}
int VerilatedReplay::replay() {
// TODO -- lockless ring buffer for separate reader/replay threads if
// dumb preloading is insufficient
if (m_preloadData) {
replayPreloadedData();
} else {
// TODO -- should I be using fstReaderIterBlocks instead? (only one CB)
// It appears that 0 is the error return code
if (fstReaderIterBlocks2(m_fstp, &VerilatedReplay::fstCallback,
&VerilatedReplay::fstCallbackVarlen, this, NULL) == 0) {
VL_PRINTF("Error iterating FST\n");
exit(-1);
}
}
// One final eval + trace since we only eval on time changes
eval();
trace();
final();
return 0;
}
void VerilatedReplay::addPreloadTime() {
ReplayData data;
data.type = ReplayData::ReplayType::Time;
data.u.time = m_preloadTime;
m_replayData.push_back(data);
}
void VerilatedReplay::loadData(ReplayData::ReplayType type, fstHandle facidx,
const unsigned char* valuep, uint32_t len) {
ReplayData data;
data.type = type;
if (type == ReplayData::ReplayType::Input) {
data.u.target = m_inputHandles[facidx].signal;
} else {
data.u.target = m_outputHandles[facidx].expected;
}
size_t bytes = (len + 7) / 8;
data.size = bytes;
// TODO -- would pre-allocating space be better? possibly hugepages if available
data.data = new vluint8_t [bytes];
copyValue(data.data, valuep, len);
m_replayData.push_back(data);
}
void VerilatedReplay::loadInput(fstHandle facidx, const unsigned char* valuep, uint32_t len) {
loadData(ReplayData::ReplayType::Input, facidx, valuep, len);
}
void VerilatedReplay::loadOutput(fstHandle facidx, const unsigned char* valuep, uint32_t len) {
loadData(ReplayData::ReplayType::Output, facidx, valuep, len);
}
void VerilatedReplay::loadData(uint64_t time, fstHandle facidx,
const unsigned char* valuep, uint32_t len) {
// TODO -- move to method and call at the very end too
if (m_preloadTime != time) {
addPreloadTime();
m_preloadTime = time;
}
if (m_outputHandles.empty() || m_inputHandles.find(facidx) != m_inputHandles.end()) {
loadInput(facidx, valuep, len);
} else {
loadOutput(facidx, valuep, len);
}
}
void VerilatedReplay::fstCb(uint64_t time, fstHandle facidx,
const unsigned char* valuep, uint32_t len) {
if (m_preloadData) {
loadData(time, facidx, valuep, len);
return;
}
// Watch for new time steps and eval before we start working on the new time
if (m_time != time) {
eval();
trace();
m_time = time;
// TODO -- use FST timescale
m_simTime = m_time;
}
if (m_outputHandles.empty() || m_inputHandles.find(facidx) != m_inputHandles.end()) {
handleInput(facidx, valuep, len);
} else {
handleOutput(facidx, valuep, len);
}
}
void VerilatedReplay::copyValue(unsigned char* to, const unsigned char* valuep, uint32_t len) {
// TODO -- is len always right, or should we use strlen() or something?
// TODO -- handle values other than 0/1, what can show up here?
vluint8_t byte = 0;
for (size_t bit = 0; bit < len; ++bit) {
char value = valuep[len - 1 - bit];
if (value == '1') byte |= 1 << (bit % 8);
if ((bit + 1) % 8 == 0 || bit == len - 1) {
*to = byte;
++to;
byte = 0;
}
}
}
void VerilatedReplay::handleInput(fstHandle facidx, const unsigned char* valuep, uint32_t len) {
vluint8_t* signal = m_inputHandles[facidx].signal;
copyValue(signal, valuep, len);
}
void VerilatedReplay::handleOutput(fstHandle facidx, const unsigned char* valuep, uint32_t len) {
FstSignal& fstSignal = m_outputHandles[facidx];
size_t bytes = (len + 7) / 8;
copyValue(fstSignal.expected, valuep, len);
}
void VerilatedReplay::fstCallbackVarlen(void* userDatap, uint64_t time, fstHandle facidx,
const unsigned char* valuep, uint32_t len) {
reinterpret_cast<VerilatedReplay*>(userDatap)->fstCb(time, facidx, valuep, len);
}
void VerilatedReplay::fstCallback(void* userDatap, uint64_t time, fstHandle facidx,
const unsigned char* valuep) {
// Cribbed from fstminer.c in the gtkwave repo
uint32_t len;
if(valuep) {
len = strlen((const char *)valuep);
} else {
len = 0;
}
fstCallbackVarlen(userDatap, time, facidx, valuep, len);
}
void VerilatedReplay::outputCheck() {
for (SignalHandleMap::iterator it = m_outputHandles.begin(); it != m_outputHandles.end(); ++it) {
size_t bytes = (it->second.bits + 7) / 8;
if (std::memcmp(it->second.expected, it->second.signal, bytes)) {
fstHandle facidx = it->first;
// TODO -- timescale, actually print out values with Verilator runtime, etc.
VL_PRINTF("Miscompare: %s @ %ld\n", m_outputs[facidx].fullName.c_str(),
m_time);
}
}
}
void VerilatedReplay::createMod() {
// TODO -- maybe get rid of the need for VM_PREFIX by generating these things
m_modp = new VM_PREFIX;
// TODO -- make VerilatedModule destructor virtual so we can delete from the base class?
#if VM_TRACE
if (m_doTrace) {
Verilated::traceEverOn(true);
m_tfp = new VerilatedFstC;
m_modp->trace(m_tfp, 99);
// TODO -- command line parameter
m_tfp->open("replay.fst");
}
#endif // VM_TRACE
}
void VerilatedReplay::eval() {
// TODO -- make eval, trace and final virtual methods of VerilatedModule?
m_modp->eval();
outputCheck();
}
void VerilatedReplay::trace() {
#if VM_TRACE
// TODO -- make this optional
if (m_tfp) m_tfp->dump(m_simTime);
#endif // VM_TRACE
}
void VerilatedReplay::final() {
m_modp->final();
}

120
include/verilated_replay.h Normal file
View File

@ -0,0 +1,120 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Copyright 2003-2020 by Todd Strader. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License.
// Version 2.0.
//
// Verilator 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.
//
//=========================================================================
///
/// \file
/// \brief Verilator: Include for replay tool
///
/// See verilator_replay
///
/// Code available from: http://www.veripool.org/verilator
///
//=========================================================================
#ifndef _VERILATED_REPLAY_H_
#define _VERILATED_REPLAY_H_ 1 ///< Header Guard
#include "verilated.h"
#include "verilated_fst_c.h"
#include "verilated_replay_common.h"
#include "gtkwave/fstapi.h"
#include <string>
#include <map>
#include <vector>
#define QUOTE(x) #x
#define MAKE_HEADER(x) QUOTE(x.h)
#include MAKE_HEADER(VM_PREFIX)
class VerilatedReplay: private VerilatedReplayCommon {
private:
struct FstSignal {
size_t bits;
vluint8_t* signal;
vluint8_t* expected;
FstSignal() {}
FstSignal(size_t _bits, vluint8_t* _signal):
bits(_bits), signal(_signal), expected(NULL) { }
FstSignal(size_t _bits, vluint8_t* _signal, vluint8_t* _expected):
bits(_bits), signal(_signal), expected(_expected) { }
};
typedef std::map<fstHandle, FstSignal> SignalHandleMap;
typedef std::map<std::string, FstSignal> SignalNameMap;
struct ReplayData {
enum ReplayType {Input, Output, Time};
size_t size;
ReplayType type;
union {
uint64_t time;
vluint8_t* target;
} u;
vluint8_t* data;
};
typedef std::vector<ReplayData> ReplayVector;
void createMod();
void addSignals();
void addInput(const std::string& fullName, vluint8_t* signal, size_t size);
void addOutput(const std::string& fullName, vluint8_t* signal, size_t size);
void outputCheck();
void eval();
void trace();
void final();
void addPreloadTime();
void loadData(ReplayData::ReplayType type, fstHandle facidx,
const unsigned char* valuep, uint32_t len);
void loadInput(fstHandle facidx, const unsigned char* valuep, uint32_t len);
void loadOutput(fstHandle facidx, const unsigned char* valuep, uint32_t len);
void replayPreloadedData();
void loadData(uint64_t time, fstHandle facidx, const unsigned char* value,
uint32_t len);
void fstCb(uint64_t time, fstHandle facidx, const unsigned char* value,
uint32_t len);
void handleInput(fstHandle facidx, const unsigned char* valuep, uint32_t len);
void handleOutput(fstHandle facidx, const unsigned char* valuep, uint32_t len);
static void fstCallback(void* userData, uint64_t time, fstHandle facidx,
const unsigned char* value);
static void fstCallbackVarlen(void* userData, uint64_t time, fstHandle facidx,
const unsigned char* value, uint32_t len);
static void copyValue(unsigned char* to, const unsigned char* valuep, uint32_t len);
std::string m_fstName;
double& m_simTime;
VM_PREFIX* m_modp;
VerilatedFstC* m_tfp;
uint64_t m_time;
uint64_t m_preloadTime;
bool m_preloadData;
bool m_doTrace;
SignalHandleMap m_inputHandles;
SignalHandleMap m_outputHandles;
SignalNameMap m_inputNames;
SignalNameMap m_outputNames;
ReplayVector m_replayData;
public:
VerilatedReplay(const std::string& fstName, double& simTime):
m_fstName(fstName), m_simTime(simTime), m_tfp(NULL),
m_preloadData(false), m_doTrace(false)
{}
~VerilatedReplay();
int init();
int replay();
void preloadData(bool value) { m_preloadData = value; }
void doTrace(bool value) { m_doTrace = value; }
};
#endif // Guard

View File

@ -0,0 +1,83 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Copyright 2003-2020 by Todd Strader. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License.
// Version 2.0.
//
// Verilator 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.
//
//=========================================================================
///
/// \file
/// \brief Verilator: Common replay code
///
/// See verilator_replay
///
/// Code available from: http://www.veripool.org/verilator
///
//=========================================================================
#include "verilated_replay_common.h"
#include <iostream>
using namespace std;
// TODO -- can we not do this?
// Include the GTKWave implementation directly
// Ugh, building with verilated_fst_c.cpp, brings this in, let's really not do this
//#define FST_CONFIG_INCLUDE "fst_config.h"
//#include "gtkwave/fastlz.c"
//#include "gtkwave/fstapi.c"
//// TODO -- use the system's LZ4 library, not this copy
//#include "gtkwave/lz4.c"
void VerilatedReplayCommon::openFst(const string& fstName) {
m_fstp = fstReaderOpen(fstName.c_str());
if (!m_fstp) {
// TODO -- a better way to fatal in either Verilator or in the runtime?
cout<<"Could not open FST: "<<fstName<<endl;
exit(-1);
}
}
void VerilatedReplayCommon::searchFst(const char* targetScope) {
const char* scope = "";
string searchScope(string(targetScope ? targetScope : ""));
while (fstHier* hierp = fstReaderIterateHier(m_fstp)) {
if (hierp->htyp == FST_HT_SCOPE) {
scope = fstReaderPushScope(m_fstp, hierp->u.scope.name, NULL);
// Just take the top scope if nothing else has been specified
if (searchScope.empty() && m_inputNames.empty() && m_outputNames.empty())
searchScope = string(scope);
} else if (hierp->htyp == FST_HT_UPSCOPE) {
scope = fstReaderPopScope(m_fstp);
} else if (hierp->htyp == FST_HT_VAR) {
string varName = string(scope) + "." + string(hierp->u.var.name);
if (string(scope) == searchScope) {
switch (hierp->u.var.direction) {
case FST_VD_INPUT:
m_inputs[hierp->u.var.handle] =
fstVar(hierp, varName);
break;
case FST_VD_OUTPUT:
m_outputs[hierp->u.var.handle] =
fstVar(hierp, varName);
break;
default: break;
}
} else if (m_inputNames.find(varName) != m_inputNames.end()) {
m_inputs[hierp->u.var.handle] = fstVar(hierp, varName);
} else if (m_outputNames.find(varName) != m_outputNames.end()) {
m_outputs[hierp->u.var.handle] = fstVar(hierp, varName);
}
}
}
}

View File

@ -0,0 +1,61 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Copyright 2003-2020 by Todd Strader. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License.
// Version 2.0.
//
// Verilator 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.
//
//=========================================================================
///
/// \file
/// \brief Verilator: Include for common replay code
///
/// See verilator_replay
///
/// Code available from: http://www.veripool.org/verilator
///
//=========================================================================
#ifndef _VERILATED_REPLAY_COMMON_H_
#define _VERILATED_REPLAY_COMMON_H_ 1 ///< Header Guard
#include "gtkwave/fstapi.h"
#include <string>
#include <map>
#include <unordered_set>
class VerilatedReplayCommon {
public:
struct fstVar {
// Can't just save the struct fstHier* that fstReadIterateHier()
// gives us because it recycles that pointer
struct fstHier hier;
std::string fullName;
fstVar(struct fstHier* _hierp, std::string _fullName):
hier(*_hierp), fullName(_fullName) {}
fstVar() {}
};
typedef std::map<fstHandle, fstVar> VarMap;
protected:
void* m_fstp;
VarMap m_inputs;
VarMap m_outputs;
std::unordered_set<std::string> m_inputNames;
std::unordered_set<std::string> m_outputNames;
public:
VerilatedReplayCommon() {}
~VerilatedReplayCommon() {}
void openFst(const std::string& fstName);
void searchFst(const char* targetScope);
void addInputName(const std::string& name) { m_inputNames.insert(name); }
void addOutputName(const std::string& name) { m_outputNames.insert(name); }
};
#endif // Guard

View File

@ -0,0 +1,59 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Copyright 2003-2020 by Todd Strader. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License.
// Version 2.0.
//
// Verilator 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.
//
//=========================================================================
///
/// \file
/// \brief Verilator: Main used by verilator_replay
///
/// This utility will replay trace files onto a verilated design.
/// It is inteded to be used in conjunction with verilator_replay.
///
/// Code available from: http://www.veripool.org/verilator
///
//=========================================================================
#include "verilated_replay.h"
// TODO -- I know this is C++11 . . . figure this out
#include <chrono>
#include <ctime>
#include <ratio>
using namespace std::chrono;
double simTime = 0;
double sc_time_stamp() {
return simTime;
}
int main(int argc, char** argv) {
// TODO -- actual arg parsing
std::string fstFilename(argv[1]);
VL_PRINTF("FST = %s\n", fstFilename.c_str());
VerilatedReplay replay(fstFilename, simTime);
// TODO -- again, arg parsing
replay.preloadData(true);
replay.doTrace(false);
if (replay.init()) exit(-1);
high_resolution_clock::time_point start = high_resolution_clock::now();
if (replay.replay()) exit(-1);
high_resolution_clock::time_point end = high_resolution_clock::now();
VL_PRINTF("Replay took %ld ns\n",
std::chrono::duration_cast<std::chrono::nanoseconds>(end-start).count());
return 0;
}

View File

@ -64,7 +64,7 @@ else
$(MAKE) -C obj_opt TGT=../$@ -f ../Makefile_obj
endif
dbg: ../bin/verilator_bin_dbg ../bin/verilator_coverage_bin_dbg
dbg: ../bin/verilator_bin_dbg ../bin/verilator_coverage_bin_dbg ../bin/verilator_replay_bin_dbg
../bin/verilator_bin_dbg: obj_dbg ../bin prefiles
$(MAKE) -C obj_dbg -j 1 TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj serial
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 -f ../Makefile_obj
@ -73,6 +73,9 @@ dbg: ../bin/verilator_bin_dbg ../bin/verilator_coverage_bin_dbg
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj serial_vlcov
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLCOV=1 -f ../Makefile_obj
../bin/verilator_replay_bin_dbg: obj_dbg ../bin prefiles
$(MAKE) -C obj_dbg TGT=../$@ VL_DEBUG=1 VL_VLREPLAY=1 -f ../Makefile_obj
prefiles::
prefiles:: config_rev.h
ifneq ($(UNDER_GIT),) # If local git tree... Else don't burden users

View File

@ -256,14 +256,31 @@ NC_OBJS += \
VLCOV_OBJS = \
VlcMain.o \
# verilator_replay
VLREPLAY_OBJS = \
V3Error.o \
V3Os.o \
V3String.o \
VlrOptions.o \
verilated_replay_common.o \
VlrGenerator.o \
VlrMain.o \
#### Linking
ifeq ($(VL_VLCOV),)
PREDEP_H = V3Ast__gen_classes.h
OBJS += $(RAW_OBJS) $(NC_OBJS)
else
ifneq ($(VL_VLCOV),)
PREDEP_H =
OBJS += $(VLCOV_OBJS)
else ifneq ($(VL_VLREPLAY),)
PREDEP_H =
OBJS += $(VLREPLAY_OBJS)
# TODO -- do we already search for this in configure?
LIBS += -lz
# TODO -- do this for verilator_coverage as well
CXXFLAGS += -D_V3ERROR_NO_GLOBAL_=1
else
PREDEP_H = V3Ast__gen_classes.h
OBJS += $(RAW_OBJS) $(NC_OBJS)
endif
V3__CONCAT.cpp: $(addsuffix .cpp, $(basename $(RAW_OBJS)))
@ -286,6 +303,9 @@ V3Number_test: V3Number_test.o
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} -c $<
%.o: %.c
$(OBJCACHE) ${CC} ${CFLAGS} ${CPPFLAGSWALL} -c $<
# TODO -- this seems terrible
verilated_replay_common.o: ../include/verilated_replay_common.cpp
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSWALL} -c $<
V3ParseLex.o: V3ParseLex.cpp V3Lexer.yy.cpp V3ParseBison.c
$(OBJCACHE) ${CXX} ${CXXFLAGS} ${CPPFLAGSPARSER} -c $<

55
src/VlrGenerator.cpp Normal file
View File

@ -0,0 +1,55 @@
#include "VlrGenerator.h"
#include "V3Error.h"
#include "gtkwave/fstapi.h"
void VlrGenerator::searchFst() {
openFst(string(m_opts.fst()));
VerilatedReplayCommon::searchFst(m_opts.scope());
}
string VlrGenerator::replayName(const string& fullName) {
string sigName(fullName);
// TODO -- add a trailing dot for the user if they don't
if (m_opts.replayTop()) {
string replayTop(m_opts.replayTop());
size_t pos = sigName.find(replayTop);
if (pos != 0) {
cout << sigName << " did not start with " << replayTop << endl;
exit(-1);
}
sigName = sigName.substr(replayTop.length());
}
return sigName;
}
void VlrGenerator::emitVltCode() {
// TODO -- use V3OutCFile
cout << "#include \"verilated_replay.h\"" << endl;
cout << endl;
cout << "void VerilatedReplay::addSignals() {" << endl;
for (VarMap::iterator it = m_inputs.begin(); it != m_inputs.end(); ++it) {
string sigName = replayName(it->second.fullName);
cout << " addInput(\"" << it->second.fullName <<
"\", &(m_modp->" << sigName <<
"), " << it->second.hier.u.var.length << ");" << endl;
// TODO -- sizof check (FST vs VLT)
}
if (m_opts.checkOutputs()) {
for (VarMap::iterator it = m_outputs.begin(); it != m_outputs.end(); ++it) {
string sigName = replayName(it->second.fullName);
cout << " addOutput(\"" << it->second.fullName <<
"\", &(m_modp->" << sigName <<
"), " << it->second.hier.u.var.length << ");" << endl;
// TODO -- sizof check (FST vs VLT)
}
}
cout << "}" << endl;
}

49
src/VlrGenerator.h Normal file
View File

@ -0,0 +1,49 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: verilator_replay: Replay code generator
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2020 by Todd Strader. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
//
// Verilator 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.
//
//*************************************************************************
#ifndef _VLRGENERATOR_H_
#define _VLRGENERATOR_H_ 1
#include "VlrOptions.h"
#include "verilated_replay_common.h"
#include <list>
class VlrGenerator: public VerilatedReplayCommon {
public:
// CONSTRUCTORS
// TODO -- passing this in the constructor is wonky, but we'd need VlrOptions to
// inherit from VerilatedReplayCommon (or something) to avoid this, but that
// would break the foo.opt.bar() convention in Verilator if VlrGenerator
// further inherited from VlrOptions
VlrGenerator(): m_opts(this) {}
~VlrGenerator() {}
// METHODS
VlrOptions& opts() { return m_opts; }
void searchFst();
std::string replayName(const std::string& fullName);
void emitVltCode();
private:
typedef std::list<std::string> StrList;
VlrOptions m_opts;
};
#endif // guard

51
src/VlrMain.cpp Normal file
View File

@ -0,0 +1,51 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: verilator_replay: main()
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2020 by Todd Strader. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
//
// Verilator 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.
//
//*************************************************************************
#include "VlrGenerator.h"
#include "V3Error.h"
// TODO -- can we not do this?
// Include the GTKWave implementation directly
#define FST_CONFIG_INCLUDE "fst_config.h"
#include "gtkwave/fastlz.c"
#include "gtkwave/fstapi.c"
// TODO -- use the system's LZ4 library, not this copy
#include "gtkwave/lz4.c"
int main(int argc, char** argv) {
VlrGenerator gen;
// Parse command line options
gen.opts().parseOptsList(argc-1, argv+1);
// Read signals from FST or signals file
if (gen.opts().fst()) {
gen.searchFst();
}
// TODO -- manually specified lists
// TODO -- check for none of the above
// Emit replay code
if (gen.opts().vlt()) {
gen.emitVltCode();
}
// TODO -- DPI and/or VPI wrappers
// TODO -- check no emitters
}

157
src/VlrOptions.cpp Normal file
View File

@ -0,0 +1,157 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: verilator_replay: Command line options
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2020 by Todd Strader. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
//
// Verilator 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.
//
//*************************************************************************
#include "VlrOptions.h"
#include "config_build.h"
#include "config_rev.h"
#include "V3Error.h"
#include "V3File.h"
#include "V3Os.h"
#include <memory>
#include <fstream>
void VlrOptions::parseOptsList(int argc, char** argv) {
// Parse parameters
// Note argc and argv DO NOT INCLUDE the filename in [0]!!!
for (int i=0; i<argc; ) {
UINFO(9, " Option: "<<argv[i]<<endl);
if (argv[i][0]=='-') {
const char* sw = argv[i];
bool flag = true;
// Allow gnu -- switches
if (sw[0]=='-' && sw[1]=='-') ++sw;
if (0) {} // TODO -- just to avoid the asymetry of one "if"?
// Single switches
else if (onoff (sw, "-check-outputs", flag/*ref*/) ) { m_checkOutputs = flag; }
else if (onoff (sw, "-vlt", flag/*ref*/) ) { m_vlt = flag; }
//// Parameterized switches
else if (!strcmp(sw, "-debug") ) {
V3Error::debugDefault(3);
}
else if (!strcmp(sw, "-debugi") && (i+1)<argc ) {
++i;
V3Error::debugDefault(atoi(argv[i]));
}
else if (!strcmp(sw, "-fst") && (i+1)<argc ) {
++i;
m_fst = argv[i];
}
else if (!strcmp(sw, "-replay-top") && (i+1)<argc ) {
++i;
m_replayTop= argv[i];
}
else if (!strcmp(sw, "-scope") && (i+1)<argc ) {
++i;
m_scope = argv[i];
}
else if (!strcmp(sw, "-signal-list") && (i+1)<argc ) {
++i;
readSignalList(argv[i]);
}
else if (!strcmp(sw, "-V") ) {
showVersion(true);
exit(0);
}
else if (!strcmp(sw, "-version") ) {
showVersion(false);
exit(0);
}
else {
v3fatal("Invalid option: "<<argv[i]);
}
++i;
} // - options
//else if (1) {
// addReadFile(argv[i]);
// ++i;
//}
else {
v3fatal("Invalid argument: "<<argv[i]);
++i;
}
}
}
void VlrOptions::showVersion(bool verbose) {
cout <<version();
cout <<endl;
if (!verbose) return;
cout <<endl;
cout << "Copyright 2020 by Todd Strader. Verilator is free software; you can\n";
cout << "redistribute it and/or modify the Verilator internals under the terms of\n";
cout << "either the GNU Lesser General Public License Version 3 or the Perl Artistic\n";
cout << "License Version 2.0.\n";
cout <<endl;
cout << "See https://verilator.org for documentation\n";
}
string VlrOptions::version() {
string ver = DTVERSION;
ver += " rev "+cvtToStr(DTVERSION_rev);
return ver;
}
// TODO -- Shared with Verilator too? Refactor into some common code.
bool VlrOptions::onoff(const char* sw, const char* arg, bool& flag) {
// if sw==arg, then return true (found it), and flag=true
// if sw=="-no-arg", then return true (found it), and flag=false
// if sw=="-noarg", then return true (found it), and flag=false
// else return false
if (arg[0]!='-') v3fatalSrc("OnOff switches must have leading dash.");
if (0==strcmp(sw, arg)) { flag = true; return true; }
else if (0==strncmp(sw, "-no", 3) && (0==strcmp(sw+3, arg+1))) { flag = false; return true; }
else if (0==strncmp(sw, "-no-", 4) && (0==strcmp(sw+4, arg+1))) { flag = false; return true; }
return false;
}
void VlrOptions::readSignalList(const char* filename) {
std::ifstream ifs(filename);
if (ifs.fail()) {
v3fatal("Cannot open -f command file: "+string(filename));
return;
}
while (!ifs.eof()) {
string line = V3Os::getline(ifs);
// Remove comments
size_t cmt = line.find("#");
if (cmt == 0) {
continue;
} else if (cmt != string::npos) {
line = line.substr(0, cmt);
}
// Parse signals
if (line.length() <= 2) continue;
string signalName = line.substr(2);
if (line.substr(0, 2) == "I ") {
m_replayp->addInputName(signalName);
} else if (line.substr(0, 2) == "O ") {
m_replayp->addOutputName(signalName);
} else {
v3fatal("Invalid signal line: "+line);
}
}
}

59
src/VlrOptions.h Normal file
View File

@ -0,0 +1,59 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: verilator_replay: Command line options
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2020 by Todd Strader. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
//
// Verilator 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.
//
//*************************************************************************
#ifndef _VLROPTIONS_H_
#define _VLROPTIONS_H_ 1
#include "verilated_replay_common.h"
#include <string>
class VlrOptions {
public:
// CONSTRUCTORS
VlrOptions(VerilatedReplayCommon* replayp):
m_checkOutputs(false), m_fst(NULL), m_replayTop(NULL), m_scope(NULL),
m_vlt(false), m_replayp(replayp)
{}
~VlrOptions() {}
// METHODS
void parseOptsList(int argc, char** argv);
bool checkOutputs() { return m_checkOutputs; }
const char* fst() { return m_fst; }
const char* replayTop() { return m_replayTop; }
const char* scope() { return m_scope; }
bool vlt() { return m_vlt; }
private:
void showVersion(bool version);
std::string version();
bool onoff(const char* sw, const char* arg, bool& flag);
void readSignalList(const char* filename);
void outputCheck();
bool m_checkOutputs;
char* m_fst;
char* m_replayTop;
char* m_scope;
bool m_vlt;
VerilatedReplayCommon* m_replayp;
};
#endif // guard

View File

@ -10,6 +10,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
v_flags2 => ["--trace-fst"], # TODO -- remove and make independent test
);
execute(

View File

@ -3,6 +3,9 @@
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2012 by Wilson Snyder.
// TODO -- don't hijack this test
`ifndef REPLAY_HACK
module t (/*AUTOARG*/
// Inputs
clk
@ -64,6 +67,8 @@ module t (/*AUTOARG*/
endmodule
`endif
module Test
(
// Inputs