From 2c25c25379a6f49026a33b9b81128029f1fddf94 Mon Sep 17 00:00:00 2001 From: Todd Strader Date: Fri, 10 Jan 2020 18:14:32 -0500 Subject: [PATCH] WIP - Add verilator_replay utility --- .gitignore | 1 + Vt_case_reducer_replay_mock.cpp | 22 ++++ include/verilated_replay.cpp | 169 ++++++++++++++++++++++++++++++ include/verilated_replay.h | 60 +++++++++++ include/verilated_replay_main.cpp | 76 ++++++++++++++ src/Makefile.in | 5 +- src/Makefile_obj.in | 22 +++- src/VlrGenerator.cpp | 47 +++++++++ src/VlrGenerator.h | 45 ++++++++ src/VlrMain.cpp | 43 ++++++++ src/VlrOptions.cpp | 111 ++++++++++++++++++++ src/VlrOptions.h | 50 +++++++++ test_regress/t/t_case_reducer.pl | 1 + test_regress/t/t_case_reducer.v | 5 + 14 files changed, 652 insertions(+), 5 deletions(-) create mode 100644 Vt_case_reducer_replay_mock.cpp create mode 100644 include/verilated_replay.cpp create mode 100644 include/verilated_replay.h create mode 100644 include/verilated_replay_main.cpp create mode 100644 src/VlrGenerator.cpp create mode 100644 src/VlrGenerator.h create mode 100644 src/VlrMain.cpp create mode 100644 src/VlrOptions.cpp create mode 100644 src/VlrOptions.h diff --git a/.gitignore b/.gitignore index 725173409..fbcd9d335 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Vt_case_reducer_replay_mock.cpp b/Vt_case_reducer_replay_mock.cpp new file mode 100644 index 000000000..fba5d875a --- /dev/null +++ b/Vt_case_reducer_replay_mock.cpp @@ -0,0 +1,22 @@ +// TODO -- will be generated by verilator_replay -- DO NOT PUSH UPSTREAM +#include "Vt_case_reducer.h" +#include "verilated_replay.h" + +void VerilatedReplay::createMod() { + m_modp = new Vt_case_reducer; + // TODO -- make VerilatedModule destructor virtual so we can delete from the base class? +} + +void VerilatedReplay::eval() { + // TODO -- make eval, trace and final virtual methods of VerilatedModule? + reinterpret_cast(m_modp)->eval(); +} + +void VerilatedReplay::trace() { + // TODO -- need VerilatedFstC, etc. + //reinterpret_cast(m_modp)->trace(); +} + +void VerilatedReplay::final() { + reinterpret_cast(m_modp)->final(); +} diff --git a/include/verilated_replay.cpp b/include/verilated_replay.cpp new file mode 100644 index 000000000..82ca28a39 --- /dev/null +++ b/include/verilated_replay.cpp @@ -0,0 +1,169 @@ +// -*- 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" + +// 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" + +// TODO -- collapse into constructor? +int VerilatedReplay::init() { + m_fstp = fstReaderOpen(m_fstName.c_str()); + if (!m_fstp) { + // TODO -- Verilator runtime way of tossing a fatal error?, see elsewhere + VL_PRINTF("Could not open FST: %s\n", m_fstName.c_str()); + exit(-1); + } + + m_time = fstReaderGetStartTime(m_fstp); + // TODO -- use FST timescale + m_simTime = m_time; + + // TODO -- this is not right, just testing + fstReaderSetFacProcessMaskAll(m_fstp); + + createMod(); + + return 0; +} + +VerilatedReplay::~VerilatedReplay() { + fstReaderClose(m_fstp); + delete(m_modp); +} + +//int VerilatedReplay::addInput(const std::string& signalName, void* dutSignal, unsigned bits) { +// for (std::vector::iterator it = m_scopep->signals.begin(); +// it != m_scopep->signals.end(); ++it) { +// if (signalName == (*it)->reference) { +// VerilatedReplaySignal* signalp = new VerilatedReplaySignal; +// signalp->dutSignal = dutSignal; +// signalp->bits = bits; +// signalp->hash = (*it)->hash; +// +// if ((*it)->size != bits) { +// VL_PRINTF("Error size mismatch on %s: trace=%d design=%d\n", +// signalName.c_str(), (*it)->size, bits); +// return -1; +// } +// +// if ((*it)->type != VCD_VAR_REG && (*it)->type != VCD_VAR_WIRE) { +// VL_PRINTF("Error unsupported signal type on %s\n", signalName.c_str()); +// return -1; +// } +// +// m_inputs.push_back(signalp); +// return 0; +// } +// } +// VL_PRINTF("Error finding signal (%s)\n", signalName.c_str()); +// return -1; +//} +// +//void VerilatedReplay::copySignal(uint8_t* signal, VCDValue* valuep) { +// VCDValueType type = valuep->get_type(); +// switch(type) { +// case VCD_SCALAR: { +// copySignalBit(signal, 0, valuep->get_value_bit()); +// break; +// } +// case VCD_VECTOR: { +// VCDBitVector* bitVector = valuep->get_value_vector(); +// unsigned length = bitVector->size(); +// for (int i = 0; i < length; ++i) { +// copySignalBit(signal, i, bitVector->at(length - i - 1)); +// } +// break; +// } +// default: { +// VL_PRINTF("Error unsupported VCD value type"); +// exit(-1); +// } +// } +//} +// +//void VerilatedReplay::copySignalBit(uint8_t* signal, unsigned offset, VCDBit bit) { +// unsigned byte = offset / 8; +// unsigned byteOffset = offset % 8; +// // TODO - more efficient byte copying +// signal[byte] &= ~(0x1 << byteOffset); +// // TODO - x's and z's? +// signal[byte] |= (bit == VCD_1 ? 0x1 : 0x0) << byteOffset; +//} + +int VerilatedReplay::replay() { + // TODO -- lockless ring buffer for separate reader/replay threads + // 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::fstCb(uint64_t time, fstHandle facidx, + const unsigned char* valuep, uint32_t len) { + // 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; + } + + VL_PRINTF("%lu %u %s\n", time, facidx, valuep); +} + +void VerilatedReplay::fstCallbackVarlen(void* userDatap, uint64_t time, fstHandle facidx, + const unsigned char* valuep, uint32_t len) { + reinterpret_cast(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); +} diff --git a/include/verilated_replay.h b/include/verilated_replay.h new file mode 100644 index 000000000..ef9dd48f2 --- /dev/null +++ b/include/verilated_replay.h @@ -0,0 +1,60 @@ +// -*- 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 "gtkwave/fstapi.h" +#include + +class VerilatedReplay { +private: + void createMod(); + void eval(); + void trace(); + void final(); + void fstCb(uint64_t time, fstHandle facidx, const unsigned char* value, + 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); + + std::string m_fstName; + double& m_simTime; + VerilatedModule* m_modp; + void* m_fstp; + uint64_t m_time; +public: + VerilatedReplay(const std::string& fstName, double& simTime): + m_fstName(fstName), m_simTime(simTime) + {} + ~VerilatedReplay(); + int init(); + int replay(); +}; + +#endif // Guard diff --git a/include/verilated_replay_main.cpp b/include/verilated_replay_main.cpp new file mode 100644 index 000000000..943b5587f --- /dev/null +++ b/include/verilated_replay_main.cpp @@ -0,0 +1,76 @@ +// -*- 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" + +double simTime = 0; +double sc_time_stamp() { + return simTime; +} + +// TODO -- should we make eval, final and trace(?) part of VerilatedModule? +// TODO -- generate this part +//#include "Vt_case_reducer.h" +//Vt_case_reducer* dutp = NULL; +//VerilatedVcdC* tfp = NULL; +//void VerilatedReplay::eval() { +// dutp->eval(); +//} +// +//void VerilatedReplay::trace() { +//#if VM_TRACE +// if (tfp) tfp->dump(simTime); +//#endif // VM_TRACE +//} +// +//void VerilatedReplay::final() { +// dutp->final(); +//} + +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); + if (replay.init()) exit(-1); + +//#if VM_TRACE +// Verilated::traceEverOn(true); +// tfp = new VerilatedFstC; +// dutp->trace(tfp, 99); +// tfp->open("replay.fst"); +// if (tfp) tfp->dump(simTime); +//#endif // VM_TRACE + + if (replay.replay()) exit(-1); + +//#if VM_TRACE +// if (tfp) tfp->close(); +//#endif // VM_TRACE + + return 0; +} diff --git a/src/Makefile.in b/src/Makefile.in index ff0c685a2..26c62bf1f 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -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 diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 4c50d5a64..946cae8e7 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -256,14 +256,28 @@ NC_OBJS += \ VLCOV_OBJS = \ VlcMain.o \ +# verilator_replay +VLREPLAY_OBJS = \ + V3Error.o \ + VlrOptions.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))) diff --git a/src/VlrGenerator.cpp b/src/VlrGenerator.cpp new file mode 100644 index 000000000..fc47c2095 --- /dev/null +++ b/src/VlrGenerator.cpp @@ -0,0 +1,47 @@ +#include "VlrGenerator.h" +#include "V3Error.h" +#include "gtkwave/fstapi.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" + +void VlrGenerator::getFstIO() { + void* fst = fstReaderOpen(opts().fst()); + const char* scope = ""; + string targetScope; + if (m_opts.scope()) targetScope = string(m_opts.scope()); + + while (struct fstHier* hier = fstReaderIterateHier(fst)) { + if (hier->htyp == FST_HT_SCOPE) { + scope = fstReaderPushScope(fst, hier->u.scope.name, NULL); + if (targetScope.empty()) targetScope = string(scope); + UINFO(3, "SCOPE "<htyp == FST_HT_UPSCOPE) { + scope = fstReaderPopScope(fst); + UINFO(3, "UPSCOPE "<htyp == FST_HT_VAR) { + if (string(scope) == targetScope) { + string varName = string(scope) + "." + string(hier->u.var.name); + switch (hier->u.var.direction) { + case FST_VD_INPUT: + UINFO(3, "VAR input "<u.var.name<u.var.name< + +class VlrGenerator { +public: + // CONSTRUCTORS + VlrGenerator() {} + ~VlrGenerator() {} + + // METHODS + VlrOptions& opts() { return m_opts; } + void getFstIO(); + void emitVltCode(); +private: + typedef std::list StrList; + + VlrOptions m_opts; + StrList m_inputs; + StrList m_outputs; +}; + +#endif // guard diff --git a/src/VlrMain.cpp b/src/VlrMain.cpp new file mode 100644 index 000000000..92ff9df55 --- /dev/null +++ b/src/VlrMain.cpp @@ -0,0 +1,43 @@ +// -*- 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" + +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.getFstIO(); + } + // 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 +} diff --git a/src/VlrOptions.cpp b/src/VlrOptions.cpp new file mode 100644 index 000000000..3afae3311 --- /dev/null +++ b/src/VlrOptions.cpp @@ -0,0 +1,111 @@ +// -*- 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" + +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 + +class VlrOptions { +public: + // CONSTRUCTORS + VlrOptions(): + m_fst(NULL), m_scope(NULL), m_vlt(false) + {} + ~VlrOptions() {} + + // METHODS + void parseOptsList(int argc, char** argv); + + const char* fst() { return m_fst; } + 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); + + const char* m_fst; + const char* m_scope; + bool m_vlt; +}; + +#endif // guard diff --git a/test_regress/t/t_case_reducer.pl b/test_regress/t/t_case_reducer.pl index 89a4e77d9..8d3fb7614 100755 --- a/test_regress/t/t_case_reducer.pl +++ b/test_regress/t/t_case_reducer.pl @@ -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( diff --git a/test_regress/t/t_case_reducer.v b/test_regress/t/t_case_reducer.v index 306786154..48e604f2a 100644 --- a/test_regress/t/t_case_reducer.v +++ b/test_regress/t/t_case_reducer.v @@ -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