From 9c00fd10de999329f23dd21b9ca860e21b7d4ac4 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 26 Aug 2012 21:13:47 -0400 Subject: [PATCH] Add --savable to support model save/restore. --- Changes | 2 + bin/verilator | 22 ++++ include/verilated.cpp | 17 ++- include/verilated.h | 38 +++--- include/verilated.v | 2 +- include/verilated_imp.h | 4 +- include/verilated_save.cpp | 224 ++++++++++++++++++++++++++++++++++ include/verilated_save.h | 234 ++++++++++++++++++++++++++++++++++++ src/Makefile_obj.in | 1 + src/V3Config.cpp | 3 +- src/V3EmitC.cpp | 96 ++++++++++++++- src/V3EmitCSyms.cpp | 28 ++++- src/V3EmitMk.cpp | 3 + src/V3EmitXml.cpp | 5 +- src/V3Options.cpp | 59 ++------- src/V3Options.h | 7 +- src/V3String.cpp | 72 +++++++++++ src/V3String.h | 91 ++++++++++++++ src/V3Task.cpp | 2 +- src/V3Undriven.cpp | 3 +- test_regress/driver.pl | 53 +++++++- test_regress/t/t_savable.pl | 28 +++++ test_regress/t/t_savable.v | 77 ++++++++++++ 23 files changed, 980 insertions(+), 91 deletions(-) create mode 100644 include/verilated_save.cpp create mode 100644 include/verilated_save.h create mode 100644 src/V3String.cpp create mode 100644 src/V3String.h create mode 100755 test_regress/t/t_savable.pl create mode 100644 test_regress/t/t_savable.v diff --git a/Changes b/Changes index 75d435268..c13305f3d 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,8 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.84*** devel +*** Add --savable to support model save/restore. [Jeremy Bennett] + *** Support '{} assignment pattern on structures, part of bug355. **** Fix double-deep parameter cell WIDTHs, bug541. [Hiroki Honda] diff --git a/bin/verilator b/bin/verilator index d1bd22e94..befeb60e4 100755 --- a/bin/verilator +++ b/bin/verilator @@ -303,6 +303,7 @@ descriptions in the next sections for more information. --private Debugging; see docs --psl Enable PSL parsing --public Debugging; see docs + --savable Enable model save-restore --sc Create SystemC output --sp Create SystemPerl output --stats Create statistics file @@ -816,6 +817,27 @@ inlining. This will also turn off inlining as if all modules had a /*verilator public_module*/, unless the module specifically enabled it with /*verilator inline_module*/. +=item --savable + +Enable including save and restore functions in the generated model. + +The user code must create a VerilatedSerialize or VerilatedDeserialze +object then calling the << or >> operators on the generated model and any +other data the process needs saved/restored. For example: + + void save_model(const char* filenamep) { + VerilatedSave os; + os.open(filenamep); + os << main_time; // user code must save the timestamp, etc + os << *topp; + } + void restore_model(const char* filenamep) { + VerilatedRestore os; + os.open(filenamep); + os >> main_time; + os >> *topp; + } + =item --sc Specifies SystemC output mode; see also --cc and -sp. diff --git a/include/verilated.cpp b/include/verilated.cpp index 37e3b1405..058df100e 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -33,14 +33,10 @@ // Global variables // Slow path variables -int Verilated::s_randReset = 0; VerilatedVoidCb Verilated::s_flushCb = NULL; // Keep below together in one cache line -int Verilated::s_debug = 0; -bool Verilated::s_calcUnusedSigs = false; -bool Verilated::s_gotFinish = false; -bool Verilated::s_assertOn = true; +Verilated::Serialized Verilated::s_s; VL_THREAD const VerilatedScope* Verilated::t_dpiScopep = NULL; VL_THREAD const char* Verilated::t_dpiFilename = ""; VL_THREAD int Verilated::t_dpiLineno = 0; @@ -81,6 +77,17 @@ void vl_fatal (const char* filename, int linenum, const char* hier, const char* } #endif +//=========================================================================== +// Overall class init + +Verilated::Serialized::Serialized() { + s_randReset = 0; + s_debug = 0; + s_calcUnusedSigs = false; + s_gotFinish = false; + s_assertOn = true; +} + //=========================================================================== // Random reset -- Only called at init time, so don't inline. diff --git a/include/verilated.h b/include/verilated.h index ac20a36d3..586e2b876 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -219,14 +219,18 @@ struct Verilated { // MEMBERS private: // Slow path variables - static int s_randReset; ///< Random reset: 0=all 0s, 1=all 1s, 2=random static VerilatedVoidCb s_flushCb; ///< Flush callback function - // Fast path - static int s_debug; ///< See accessors... only when VL_DEBUG set - static bool s_calcUnusedSigs; ///< Waves file on, need all signals calculated - static bool s_gotFinish; ///< A $finish statement executed - static bool s_assertOn; ///< Assertions are enabled + static struct Serialized { // All these members serialized/deserialized + // Slow path + int s_randReset; ///< Random reset: 0=all 0s, 1=all 1s, 2=random + // Fast path + int s_debug; ///< See accessors... only when VL_DEBUG set + bool s_calcUnusedSigs; ///< Waves file on, need all signals calculated + bool s_gotFinish; ///< A $finish statement executed + bool s_assertOn; ///< Assertions are enabled + Serialized(); + } s_s; static VL_THREAD const VerilatedScope* t_dpiScopep; ///< DPI context scope static VL_THREAD const char* t_dpiFilename; ///< DPI context filename @@ -241,29 +245,29 @@ public: /// 0 = Set to zeros /// 1 = Set all bits to one /// 2 = Randomize all bits - static void randReset(int val) { s_randReset=val; } - static int randReset() { return s_randReset; } ///< Return randReset value + static void randReset(int val) { s_s.s_randReset=val; } + static int randReset() { return s_s.s_randReset; } ///< Return randReset value /// Enable debug of internal verilated code - static inline void debug(int level) { s_debug = level; } + static inline void debug(int level) { s_s.s_debug = level; } #ifdef VL_DEBUG - static inline int debug() { return s_debug; } ///< Return debug value + static inline int debug() { return s_s.s_debug; } ///< Return debug value #else static inline int debug() { return 0; } ///< Constant 0 debug, so C++'s optimizer rips up #endif /// Enable calculation of unused signals - static void calcUnusedSigs(bool flag) { s_calcUnusedSigs=flag; } - static bool calcUnusedSigs() { return s_calcUnusedSigs; } ///< Return calcUnusedSigs value + static void calcUnusedSigs(bool flag) { s_s.s_calcUnusedSigs=flag; } + static bool calcUnusedSigs() { return s_s.s_calcUnusedSigs; } ///< Return calcUnusedSigs value /// Did the simulation $finish? - static void gotFinish(bool flag) { s_gotFinish=flag; } - static bool gotFinish() { return s_gotFinish; } ///< Return if got a $finish + static void gotFinish(bool flag) { s_s.s_gotFinish=flag; } + static bool gotFinish() { return s_s.s_gotFinish; } ///< Return if got a $finish /// Allow traces to at some point be enabled (disables some optimizations) static void traceEverOn(bool flag) { if (flag) { calcUnusedSigs(flag); } } /// Enable/disable assertions - static void assertOn(bool flag) { s_assertOn=flag; } - static bool assertOn() { return s_assertOn; } + static void assertOn(bool flag) { s_s.s_assertOn=flag; } + static bool assertOn() { return s_s.s_assertOn; } /// Flush callback for VCD waves static void flushCb(VerilatedVoidCb cb); static void flushCall() { if (s_flushCb) (*s_flushCb)(); } @@ -293,6 +297,8 @@ public: static const char* dpiFilenamep() { return t_dpiFilename; } static int dpiLineno() { return t_dpiLineno; } static int exportFuncNum(const char* namep); + static size_t serializedSize() { return sizeof(s_s); } + static void* serializedPtr() { return &s_s; } }; //========================================================================= diff --git a/include/verilated.v b/include/verilated.v index 74b1c8cc2..0108c6899 100644 --- a/include/verilated.v +++ b/include/verilated.v @@ -22,7 +22,7 @@ `define _VERILATED_V_ 1 // Hide verilator pragmas from other tools - `ifdef verilator `else + `ifdef VERILATOR `else `define coverage_block_off `endif diff --git a/include/verilated_imp.h b/include/verilated_imp.h index 764371ad3..a539ce03f 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -55,7 +55,9 @@ class VerilatedImp { // MEMBERS static VerilatedImp s_s; ///< Static Singleton; One and only static this - ArgVec m_argVec; ///< Argument list + // Nothing here is save-restored; users expected to re-register appropriately + + ArgVec m_argVec; ///< Argument list (NOT save-restored, may want different results) bool m_argVecLoaded; ///< Ever loaded argument list UserMap m_userMap; ///< Map of <(scope,userkey), userData> ScopeNameMap m_nameMap; ///< Map of diff --git a/include/verilated_save.cpp b/include/verilated_save.cpp new file mode 100644 index 000000000..e01b0d9fe --- /dev/null +++ b/include/verilated_save.cpp @@ -0,0 +1,224 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// THIS MODULE IS PUBLICLY LICENSED +// +// Copyright 2001-2012 by Wilson Snyder. 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. +// +// This 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 C++ Tracing in VCD Format +/// +/// AUTHOR: Wilson Snyder +/// +//============================================================================= + +#include "verilatedos.h" +#include "verilated.h" +#include "verilated_save.h" + +#include +#include + +#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) +# include +#else +# include +#endif + +// CONSTANTS +static const char* VLTSAVE_HEADER_STR = "verilatorsave01\n"; ///< Value of first bytes of each file +static const char* VLTSAVE_TRAILER_STR = "vltsaved"; ///< Value of last bytes of each file + +//============================================================================= +//============================================================================= +//============================================================================= +// Searalization + +bool VerilatedDeserialize::readDiffers (const void* __restrict datap, size_t size) { + bufferCheck(); + const vluint8_t* __restrict dp = (const vluint8_t* __restrict)datap; + vluint8_t miss = 0; + while (size--) { + miss |= (*dp++ ^ *m_cp++); + } + return (miss!=0); +} + +VerilatedDeserialize& VerilatedDeserialize::readAssert (const void* __restrict datap, size_t size) { + if (VL_UNLIKELY(readDiffers(datap,size))) { + string msg = (string)"Can't deserialize save-restore file as was made from different model"; + vl_fatal(filename().c_str(), 0, "", msg.c_str()); + close(); + } + return *this; // For function chaining +} + +void VerilatedSerialize::header() { + VerilatedSerialize& os = *this; // So can cut and paste standard << code below + assert((strlen(VLTSAVE_HEADER_STR) & 7) == 0); // Keep aligned + os.write(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR)); + + // Verilated doesn't do it itself, as if we're not using save/restore + // it doesn't need to compile this stuff in + os.write(Verilated::serializedPtr(), Verilated::serializedSize()); +} + +void VerilatedDeserialize::header() { + VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below + if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, strlen(VLTSAVE_HEADER_STR)))) { + string msg = (string)"Can't deserialize; file has wrong header signature"; + vl_fatal(filename().c_str(), 0, "", msg.c_str()); + close(); + } + os.read(Verilated::serializedPtr(), Verilated::serializedSize()); +} + +void VerilatedSerialize::trailer() { + VerilatedSerialize& os = *this; // So can cut and paste standard << code below + assert((strlen(VLTSAVE_TRAILER_STR) & 7) == 0); // Keep aligned + os.write(VLTSAVE_TRAILER_STR, strlen(VLTSAVE_TRAILER_STR)); +} + +void VerilatedDeserialize::trailer() { + VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below + if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, strlen(VLTSAVE_TRAILER_STR)))) { + string msg = (string)"Can't deserialize; file has wrong end-of-file signature"; + vl_fatal(filename().c_str(), 0, "", msg.c_str()); + close(); + } +} + +//============================================================================= +//============================================================================= +//============================================================================= +// Opening/Closing + +void VerilatedSave::open (const char* filenamep) { + if (isOpen()) return; + VL_DEBUG_IF(VL_PRINTF("-vltSave: opening save file %s\n",filenamep);); + + if (filenamep[0]=='|') { + assert(0); // Not supported yet. + } else { + m_fd = ::open (filenamep, O_CREAT|O_WRONLY|O_TRUNC|O_LARGEFILE|O_NONBLOCK + , 0666); + if (m_fd<0) { + // User code can check isOpen() + m_isOpen = false; + return; + } + } + m_isOpen = true; + m_filename = filenamep; + m_cp = m_bufp; + header(); +} + +void VerilatedRestore::open (const char* filenamep) { + if (isOpen()) return; + VL_DEBUG_IF(VL_PRINTF("-vltRestore: opening restore file %s\n",filenamep);); + + if (filenamep[0]=='|') { + assert(0); // Not supported yet. + } else { + m_fd = ::open (filenamep, O_CREAT|O_RDONLY|O_LARGEFILE + , 0666); + if (m_fd<0) { + // User code can check isOpen() + m_isOpen = false; + return; + } + } + m_isOpen = true; + m_filename = filenamep; + m_cp = m_bufp; + m_endp = m_bufp; + header(); +} + +void VerilatedSave::close () { + if (!isOpen()) return; + trailer(); + flush(); + m_isOpen = false; + ::close(m_fd); // May get error, just ignore it +} + +void VerilatedRestore::close () { + if (!isOpen()) return; + trailer(); + flush(); + m_isOpen = false; + ::close(m_fd); // May get error, just ignore it +} + +//============================================================================= +// Buffer management + +void VerilatedSave::flush() { + if (VL_UNLIKELY(!isOpen())) return; + vluint8_t* wp = m_bufp; + while (1) { + ssize_t remaining = (m_cp - wp); + if (remaining==0) break; + errno = 0; + ssize_t got = ::write (m_fd, wp, remaining); + if (got>0) { + wp += got; + } else if (got < 0) { + if (errno != EAGAIN && errno != EINTR) { + // write failed, presume error (perhaps out of disk space) + string msg = string(__FUNCTION__)+": "+strerror(errno); + vl_fatal("",0,"",msg.c_str()); + close(); + break; + } + } + } + m_cp = m_bufp; // Reset buffer +} + +void VerilatedRestore::fill() { + if (VL_UNLIKELY(!isOpen())) return; + // Move remaining characters down to start of buffer. (No memcpy, overlaps allowed) + vluint8_t* rp = m_bufp; + for (vluint8_t* sp=m_cp; rp < m_endp;) *rp++ = *sp++; // Overlaps + m_endp = m_bufp + (m_endp - m_cp); + m_cp = m_bufp; // Reset buffer + // Read into buffer starting at m_endp + while (1) { + ssize_t remaining = (m_bufp+bufferSize() - m_endp); + if (remaining==0) break; + errno = 0; + ssize_t got = ::read (m_fd, m_endp, remaining); + if (got>0) { + m_endp += got; + } else if (got < 0) { + if (errno != EAGAIN && errno != EINTR) { + // write failed, presume error (perhaps out of disk space) + string msg = string(__FUNCTION__)+": "+strerror(errno); + vl_fatal("",0,"",msg.c_str()); + close(); + break; + } + } else { // got==0, EOF + // Fill buffer from here to end with NULLs so reader's don't need to check eof each character. + while (m_endp < m_bufp+bufferSize()) *m_endp++ = '\0'; + break; + } + } +} + +//============================================================================= +// Serialization of types + + diff --git a/include/verilated_save.h b/include/verilated_save.h new file mode 100644 index 000000000..c8a83f9ed --- /dev/null +++ b/include/verilated_save.h @@ -0,0 +1,234 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// THIS MODULE IS PUBLICLY LICENSED +// +// Copyright 2012-2012 by Wilson Snyder. 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. +// +// This 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 Save-restore serialization of verilated modules +/// +/// AUTHOR: Wilson Snyder +/// +//============================================================================= + +#ifndef _VERILATED_SAVE_C_H_ +#define _VERILATED_SAVE_C_H_ 1 + +#include "verilatedos.h" + +#include +using namespace std; + +//============================================================================= +// VerilatedSerialBase - internal base class for common code between VerilatedSerialize and VerilatedDeserialize + +class VerilatedSerialBase { +protected: + // MEMBERS + // For speed, keep m_cp as the first member of this structure + vluint8_t* m_cp; ///< Current pointer into m_bufp buffer + vluint8_t* m_bufp; ///< Output buffer + bool m_isOpen; ///< True indicates open file/stream + string m_filename; + + inline static size_t bufferSize() { return 256*1024; } // See below for slack calculation + inline static size_t bufferInsertSize() { return 16*1024; } + + // CREATORS + VerilatedSerialBase() { + m_isOpen = false; + m_bufp = new vluint8_t [bufferSize()]; + m_cp = m_bufp; + } +public: + // CREATORS + virtual ~VerilatedSerialBase() { + close(); + if (m_bufp) { delete m_bufp; m_bufp=NULL; } + } + // METHODS + bool isOpen() const { return m_isOpen; } + string filename() const { return m_filename; } + virtual void close() { flush(); } + virtual void flush() {} +}; + +//============================================================================= +// VerilatedSerialize - convert structures to a stream representation + +class VerilatedSerialize : public VerilatedSerialBase { +protected: + virtual void close() { flush(); } + virtual void flush() {} + void header(); + void trailer(); +public: + // CREATORS + VerilatedSerialize() {} + virtual ~VerilatedSerialize() { close(); } + // METHODS + VerilatedSerialize& bufferCheck() { + // Flush the write buffer if there's not enough space left for new information + // We only call this once per vector, so we need enough slop for a very wide "b###" line + if (VL_UNLIKELY(m_cp > (m_bufp+(bufferSize()-bufferInsertSize())))) { + flush(); + } + return *this; // For function chaining + } + inline VerilatedSerialize& write (const void* __restrict datap, size_t size) { + const vluint8_t* __restrict dp = (const vluint8_t* __restrict)datap; + while (size) { + bufferCheck(); + size_t blk = size; if (blk>bufferInsertSize()) blk = bufferInsertSize(); + const vluint8_t* __restrict maxp = dp + blk; + while (dp < maxp) *m_cp++ = *dp++; + size -= blk; + } + return *this; // For function chaining + } +}; + +//============================================================================= +// VerilatedDeserial - load structures from a stream representation + +class VerilatedDeserialize : public VerilatedSerialBase { +protected: + vluint8_t* m_endp; ///< Last valid byte in m_bufp buffer + virtual void fill() = 0; + void header(); + void trailer(); +public: + // CREATORS + VerilatedDeserialize() {} + virtual ~VerilatedDeserialize() { close(); } + // METHODS + inline VerilatedDeserialize& read (void* __restrict datap, size_t size) { + vluint8_t* __restrict dp = (vluint8_t* __restrict)datap; + while (size) { + bufferCheck(); + size_t blk = size; if (blk>bufferInsertSize()) blk = bufferInsertSize(); + const vluint8_t* __restrict maxp = dp + blk; + while (dp < maxp) *dp++ = *m_cp++; + size -= blk; + } + return *this; // For function chaining + } + // Read a datum and compare with expected value + bool readDiffers (const void* __restrict datap, size_t size); + VerilatedDeserialize& readAssert (const void* __restrict datap, size_t size); + VerilatedDeserialize& readAssert (vluint64_t data) { return readAssert(&data, sizeof(data)); } + VerilatedDeserialize& bufferCheck() { + // Flush the write buffer if there's not enough space left for new information + // We only call this once per vector, so we need enough slop for a very wide "b###" line + if (VL_UNLIKELY((m_cp+bufferInsertSize()) > m_endp)) { + fill(); + } + return *this; // For function chaining + } +}; + +//============================================================================= +// VerilatedSave - serialize to a file + +class VerilatedSave : public VerilatedSerialize { +private: + int m_fd; ///< File descriptor we're writing to + +public: + // CREATORS + VerilatedSave() {} + virtual ~VerilatedSave() { close(); } + // METHODS + void open(const char* filenamep); ///< Open the file; call isOpen() to see if errors + void open(const string& filename) { open(filename.c_str()); } + virtual void close(); + virtual void flush(); +}; + +//============================================================================= +// VerilatedRestore - deserialize from a file + +class VerilatedRestore : public VerilatedDeserialize { +private: + int m_fd; ///< File descriptor we're writing to + +public: + // CREATORS + VerilatedRestore() {} + virtual ~VerilatedRestore() { close(); } + + // METHODS + void open(const char* filenamep); ///< Open the file; call isOpen() to see if errors + void open(const string& filename) { open(filename.c_str()); } + virtual void close(); + virtual void flush() {} + virtual void fill(); +}; + +//============================================================================= + +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint64_t& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint64_t& rhs){ + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint32_t& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint32_t& rhs) { + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint16_t& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint16_t& rhs) { + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, vluint8_t& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, vluint8_t& rhs) { + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, bool& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, bool& rhs) { + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, double& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, double& rhs) { + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, float& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) { + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, string& rhs) { + vluint32_t len=rhs.length(); + os<>(VerilatedDeserialize& os, string& rhs) { + vluint32_t len; + os>>len; + rhs.resize(len); + return os.read((void*)rhs.data(), len); +} + +#endif // guard diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index b46299235..d07cb9dbf 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -219,6 +219,7 @@ RAW_OBJS = \ V3SplitAs.o \ V3Stats.o \ V3StatsReport.o \ + V3String.o \ V3Subst.o \ V3Table.o \ V3Task.o \ diff --git a/src/V3Config.cpp b/src/V3Config.cpp index 99c87244a..bc0e4e376 100644 --- a/src/V3Config.cpp +++ b/src/V3Config.cpp @@ -25,6 +25,7 @@ #include #include "V3Global.h" +#include "V3String.h" #include "V3Config.h" //###################################################################### @@ -89,7 +90,7 @@ class V3ConfigIgnores { it = m_ignFiles.find(filename); // Make new list for this file of all matches for (IgnFiles::iterator fnit = m_ignWilds.begin(); fnit != m_ignWilds.end(); ++fnit) { - if (V3Options::wildmatch(filename.c_str(), fnit->first.c_str())) { + if (VString::wildmatch(filename.c_str(), fnit->first.c_str())) { for (IgnLines::iterator lit = fnit->second.begin(); lit != fnit->second.end(); ++lit) { it->second.insert(*lit); } diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 2a158e2e2..9aa214a8c 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -29,6 +29,7 @@ #include #include "V3Global.h" +#include "V3String.h" #include "V3EmitC.h" #include "V3EmitCBase.h" @@ -842,6 +843,7 @@ class EmitCImp : EmitCStmts { void emitCoverageDecl(AstNodeModule* modp); void emitCoverageImp(AstNodeModule* modp); void emitDestructorImp(AstNodeModule* modp); + void emitSavableImp(AstNodeModule* modp); void emitTextSection(AstType type); void emitIntFuncDecls(AstNodeModule* modp); // High level @@ -1436,7 +1438,7 @@ void EmitCImp::emitCoverageImp(AstNodeModule* modp) { // compatible, and have a common wrapper. puts("void "+modClassName(m_modp)+"::__vlCoverInsert(uint32_t* countp, bool enable, const char* filenamep, int lineno, int column,\n"); puts( "const char* hierp, const char* pagep, const char* commentp) {\n"); - puts( "static uint32_t fake_zero_count = 0;\n"); + puts( "static uint32_t fake_zero_count = 0;\n"); // static doesn't need save-restore as constant puts( "if (!enable) countp = &fake_zero_count;\n"); // Used for second++ instantiation of identical bin puts( "*countp = 0;\n"); puts( "SP_COVER_INSERT(countp,"); @@ -1459,6 +1461,79 @@ void EmitCImp::emitDestructorImp(AstNodeModule* modp) { puts("}\n"); } +void EmitCImp::emitSavableImp(AstNodeModule* modp) { + if (v3Global.opt.savable() ) { + puts("\n// Savable\n"); + for (int de=0; de<2; ++de) { + string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; + string funcname = de ? "__Vdeserialize" : "__Vserialize"; + string writeread = de ? "read" : "write"; + string op = de ? ">>" : "<<"; + puts("void "+modClassName(modp)+"::"+funcname+"("+classname+"& os) {\n"); + // Place a computed checksum to insure proper structure save/restore formatting + // OK if this hash includes some things we won't dump, since just looking for loading the wrong model + VHashFnv hash; + for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstVar* varp = nodep->castVar()) { + hash.hash(varp->name()); + hash.hash(varp->dtypep()->width()); + } + } + ofp()->printf( "vluint64_t __Vcheckval = VL_ULL(0x%" VL_PRI64 "x);\n", + hash.value()); + if (de) { + puts("os.readAssert(__Vcheckval);\n"); + } else { + puts("os<<__Vcheckval;\n"); + } + + // Save all members + if (v3Global.opt.inhibitSim()) puts("os"+op+"__Vm_inhibitSim;\n"); + for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (AstVar* varp = nodep->castVar()) { + if (varp->isIO() && modp->isTop() && optSystemC()) { + // System C top I/O doesn't need loading, as the lower level subinst code does it. + } + else if (varp->isParam()) {} + else if (varp->isStatic() && varp->isConst()) {} + else { + int vects = 0; + // This isn't very robust and may need cleanup for other data types + for (AstArrayDType* arrayp=varp->dtypeSkipRefp()->castArrayDType(); arrayp; + arrayp = arrayp->subDTypep()->skipRefp()->castArrayDType()) { + int vecnum = vects++; + if (arrayp->msb() < arrayp->lsb()) varp->v3fatalSrc("Should have swapped msb & lsb earlier."); + string ivar = string("__Vi")+cvtToStr(vecnum); + // MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block + puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(0)+";"); + puts(" for (; "+ivar+"<"+cvtToStr(arrayp->elementsConst())); + puts("; ++"+ivar+") {\n"); + } + if (varp->basicp() && (varp->basicp()->keyword() == AstBasicDTypeKwd::STRING + || !varp->basicp()->isWide())) { + puts("os"+op+varp->name()); + for (int v=0; vname()); + for (int v=0; vname()); + for (int v=0; visTop()) { // Save the children + puts( "__VlSymsp->"+funcname+"(os);\n"); + } + puts("}\n"); + } + } +} + void EmitCImp::emitStaticDecl(AstNodeModule* modp) { // Need implementation here. Be careful of alignment code; needs to be uniquified // with module name to avoid multiple symbols. @@ -1675,8 +1750,12 @@ void EmitCImp::emitInt(AstNodeModule* modp) { } else { puts("#include \"verilated.h\"\n"); } + if (v3Global.opt.savable()) { + puts("#include \"verilated_save.h\"\n"); + } if (v3Global.opt.coverage()) { puts("#include \"SpCoverage.h\"\n"); + if (v3Global.opt.savable()) v3error("--coverage and --savable not supported together"); } if (v3Global.needHInlines()) { // Set by V3EmitCInlines; should have been called before us puts("#include \""+topClassName()+"__Inlines.h\"\n"); @@ -1842,10 +1921,22 @@ void EmitCImp::emitInt(AstNodeModule* modp) { puts("static void traceFull ("+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code);\n"); puts("static void traceChg ("+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code);\n"); } + if (v3Global.opt.savable()) { + puts("void __Vserialize(VerilatedSerialize& os);\n"); + puts("void __Vdeserialize(VerilatedDeserialize& os);\n"); + puts("\n"); + } - puts("} VL_ATTR_ALIGNED(64);\n"); + puts("} VL_ATTR_ALIGNED(128);\n"); puts("\n"); + // Save/restore + if (v3Global.opt.savable() && modp->isTop()) { + puts("inline VerilatedSerialize& operator<<(VerilatedSerialize& os, "+modClassName(modp)+"& rhs) {rhs.__Vserialize(os); return os;}\n"); + puts("inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, "+modClassName(modp)+"& rhs) {rhs.__Vdeserialize(os); return os;}\n"); + puts("\n"); + } + // finish up h-file if (!optSystemPerl()) { puts("#endif /*guard*/\n"); @@ -1893,6 +1984,7 @@ void EmitCImp::emitImp(AstNodeModule* modp) { emitCtorImp(modp); emitConfigureImp(modp); emitDestructorImp(modp); + emitSavableImp(modp); emitCoverageImp(modp); } diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index aa88ff135..0f189a26a 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -339,8 +339,13 @@ void EmitCSyms::emitSymHdr() { puts("\n// METHODS\n"); puts("inline const char* name() { return __Vm_namep; }\n"); puts("inline bool getClearActivity() { bool r=__Vm_activity; __Vm_activity=false; return r;}\n"); + if (v3Global.opt.savable() ) { + puts("void __Vserialize(VerilatedSerialize& os);\n"); + puts("void __Vdeserialize(VerilatedDeserialize& os);\n"); + } puts("\n"); puts("} VL_ATTR_ALIGNED(64);\n"); + puts("\n"); puts("#endif /*guard*/\n"); } @@ -498,7 +503,28 @@ void EmitCSyms::emitSymImp() { } puts("}\n"); - puts("\n"); + + if (v3Global.opt.savable() ) { + puts("\n"); + for (int de=0; de<2; ++de) { + string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize"; + string funcname = de ? "__Vdeserialize" : "__Vserialize"; + string op = de ? ">>" : "<<"; + puts("void "+symClassName()+"::"+funcname+"("+classname+"& os) {\n"); + puts( "// LOCAL STATE\n"); + // __Vm_namep presumably already correct + puts( "os"+op+"__Vm_activity;\n"); + puts( "os"+op+"__Vm_didInit;\n"); + puts( "// SUBCELL STATE\n"); + for (vector::iterator it = m_scopes.begin(); it != m_scopes.end(); ++it) { + AstScope* scopep = it->first; AstNodeModule* modp = it->second; + if (!modp->isTop()) { + puts( scopep->nameDotless()+"."+funcname+"(os);\n"); + } + } + puts("}\n"); + } + } } //###################################################################### diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index 0b23b2e49..3dfa870dc 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -80,6 +80,9 @@ public: if (v3Global.dpi()) { putMakeClassEntry(of, "verilated_dpi.cpp"); } + if (v3Global.opt.savable()) { + putMakeClassEntry(of, "verilated_save.cpp"); + } if (v3Global.opt.systemPerl()) { putMakeClassEntry(of, "Sp.cpp"); // Note Sp.cpp includes SpTraceVcdC } diff --git a/src/V3EmitXml.cpp b/src/V3EmitXml.cpp index 743bc3e1c..56b4b9f29 100644 --- a/src/V3EmitXml.cpp +++ b/src/V3EmitXml.cpp @@ -29,6 +29,7 @@ #include #include "V3Global.h" +#include "V3String.h" #include "V3EmitXml.h" #include "V3EmitCBase.h" @@ -64,12 +65,12 @@ class EmitXmlFileVisitor : public EmitCBaseVisitor { // XML methods void outputTag(AstNode* nodep, string tag) { - if (tag=="") tag = V3Options::downcase(nodep->typeName()); + if (tag=="") tag = VString::downcase(nodep->typeName()); puts("<"+tag+" "+nodep->fileline()->xml()); if (nodep->name()!="") { puts(" name="); putsQuoted(nodep->prettyName()); } } void outputChildrenEnd(AstNode* nodep, string tag) { - if (tag=="") tag = V3Options::downcase(nodep->typeName()); + if (tag=="") tag = VString::downcase(nodep->typeName()); if (nodep->op1p() || nodep->op2p() || nodep->op3p() || nodep->op4p()) { puts(">\n"); nodep->iterateChildren(*this); diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 073514618..53e40dd57 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -34,6 +34,7 @@ #include #include "V3Global.h" +#include "V3String.h" #include "V3Options.h" #include "V3Error.h" #include "V3File.h" @@ -367,7 +368,7 @@ void V3Options::filePathLookedMsg(FileLine* fl, const string& modname) { void V3Options::unlinkRegexp(const string& dir, const string& regexp) { if (DIR* dirp = opendir(dir.c_str())) { while (struct dirent* direntp = readdir(dirp)) { - if (wildmatch(direntp->d_name, regexp.c_str())) { + if (VString::wildmatch(direntp->d_name, regexp.c_str())) { string fullname = dir + "/" + string(direntp->d_name); unlink (fullname.c_str()); } @@ -433,9 +434,9 @@ string V3Options::getenvSYSTEMC_ARCH() { #else struct utsname uts; uname(&uts); - string sysname = downcase(uts.sysname); // aka 'uname -s' - if (wildmatch(sysname.c_str(), "*solaris*")) { var = "gccsparcOS5"; } - else if (wildmatch(sysname.c_str(), "*cygwin*")) { var ="cygwin"; } + string sysname = VString::downcase(uts.sysname); // aka 'uname -s' + if (VString::wildmatch(sysname.c_str(), "*solaris*")) { var = "gccsparcOS5"; } + else if (VString::wildmatch(sysname.c_str(), "*cygwin*")) { var ="cygwin"; } else { var = "linux"; } #endif setenvStr("SYSTEMC_ARCH", var,"From sysname '"+sysname+"'"); @@ -542,54 +543,6 @@ string V3Options::getenvVERILATOR_ROOT() { return var; } -//###################################################################### -// Wildcard - -// Double procedures, inlined, unrolls loop much better -inline bool V3Options::wildmatchi(const char* s, const char* p) { - for ( ; *p; s++, p++) { - if (*p!='*') { - if (((*s)!=(*p)) && *p != '?') - return false; - } - else { - // Trailing star matches everything. - if (!*++p) return true; - while (wildmatch(s, p) == false) - if (*++s == '\0') - return false; - return true; - } - } - return (*s == '\0'); -} - -bool V3Options::wildmatch(const char* s, const char* p) { - for ( ; *p; s++, p++) { - if (*p!='*') { - if (((*s)!=(*p)) && *p != '?') - return false; - } - else { - // Trailing star matches everything. - if (!*++p) return true; - while (wildmatchi(s, p) == false) - if (*++s == '\0') - return false; - return true; - } - } - return (*s == '\0'); -} - -string V3Options::downcase(const string& str) { - string out = str; - for (string::iterator pos = out.begin(); pos != out.end(); ++pos) { - *pos = tolower(*pos); - } - return out; -} - //###################################################################### // V3 Options accessors @@ -737,6 +690,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char else if ( onoff (sw, "-profile-cfuncs", flag/*ref*/) ) { m_profileCFuncs = flag; } else if ( onoff (sw, "-psl", flag/*ref*/) ) { m_psl = flag; } else if ( onoff (sw, "-public", flag/*ref*/) ) { m_public = flag; } + else if ( onoff (sw, "-savable", flag/*ref*/) ) { m_savable = flag; } else if ( !strcmp (sw, "-sc") ) { m_outFormatOk = true; m_systemC = true; m_systemPerl = false; } else if ( onoff (sw, "-skip-identical", flag/*ref*/) ) { m_skipIdentical = flag; } else if ( !strcmp (sw, "-sp") ) { m_outFormatOk = true; m_systemC = true; m_systemPerl = true; } @@ -1181,6 +1135,7 @@ V3Options::V3Options() { m_preprocOnly = false; m_psl = false; m_public = false; + m_savable = false; m_skipIdentical = true; m_stats = false; m_systemC = false; diff --git a/src/V3Options.h b/src/V3Options.h index cc84467af..c4136e58f 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -115,6 +115,7 @@ class V3Options { bool m_profileCFuncs;// main switch: --profile-cfuncs bool m_psl; // main switch: --psl bool m_public; // main switch: --public + bool m_savable; // main switch: --savable bool m_systemC; // main switch: --sc: System C instead of simple C++ bool m_skipIdentical;// main switch: --skip-identical bool m_systemPerl; // main switch: --sp: System Perl instead of SystemC (m_systemC also set) @@ -192,7 +193,6 @@ class V3Options { string parseFileArg(const string& optdir, const string& relfilename); string filePathCheckOneDir(const string& modname, const string& dirname); - static bool wildmatchi(const char* s, const char* p); static string getenvStr(const string& envvar, const string& defaultValue); static void setenvStr(const string& envvar, const string& value, const string& why); static string getenvSYSTEMPERLGuts(); @@ -223,6 +223,7 @@ class V3Options { bool systemPerl() const { return m_systemPerl; } bool usingSystemCLibs() const { return !lintOnly() && (systemPerl() || systemC()); } bool usingSystemPerlLibs() const { return !lintOnly() && (systemPerl() || coverage()); } + bool savable() const { return m_savable; } bool skipIdentical() const { return m_skipIdentical; } bool stats() const { return m_stats; } bool assertOn() const { return m_assert; } // assertOn as __FILE__ may be defined @@ -321,10 +322,6 @@ class V3Options { void parseOptsList (FileLine* fl, const string& optdir, int argc, char** argv); void parseOptsFile (FileLine* fl, const string& filename, bool rel); - // METHODS (generic string utilities) - static bool wildmatch(const char* s, const char* p); - static string downcase(const string& str); - // METHODS (generic file utilities) static string filenameFromDirBase (const string& dir, const string& basename); static string filenameNonDir (const string& filename); ///< Return non-directory part of filename diff --git a/src/V3String.cpp b/src/V3String.cpp new file mode 100644 index 000000000..d1b01c8d8 --- /dev/null +++ b/src/V3String.cpp @@ -0,0 +1,72 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Options parsing +// +// Code available from: http://www.veripool.org/verilator +// +//************************************************************************* +// +// Copyright 2003-2012 by Wilson Snyder. 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 "config_build.h" +#include "verilatedos.h" + +#include "V3String.h" + +//###################################################################### +// Wildcard + +// Double procedures, inlined, unrolls loop much better +inline bool VString::wildmatchi(const char* s, const char* p) { + for ( ; *p; s++, p++) { + if (*p!='*') { + if (((*s)!=(*p)) && *p != '?') + return false; + } + else { + // Trailing star matches everything. + if (!*++p) return true; + while (wildmatch(s, p) == false) + if (*++s == '\0') + return false; + return true; + } + } + return (*s == '\0'); +} + +bool VString::wildmatch(const char* s, const char* p) { + for ( ; *p; s++, p++) { + if (*p!='*') { + if (((*s)!=(*p)) && *p != '?') + return false; + } + else { + // Trailing star matches everything. + if (!*++p) return true; + while (wildmatchi(s, p) == false) + if (*++s == '\0') + return false; + return true; + } + } + return (*s == '\0'); +} + +string VString::downcase(const string& str) { + string out = str; + for (string::iterator pos = out.begin(); pos != out.end(); ++pos) { + *pos = tolower(*pos); + } + return out; +} diff --git a/src/V3String.h b/src/V3String.h new file mode 100644 index 000000000..29482742a --- /dev/null +++ b/src/V3String.h @@ -0,0 +1,91 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: String manipulation +// +// Code available from: http://www.veripool.org/verilator +// +//************************************************************************* +// +// Copyright 2003-2012 by Wilson Snyder. 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 _V3STRING_H_ +#define _V3STRING_H_ 1 + +#include "config_build.h" +#include "verilatedos.h" +#include + +//###################################################################### +// VString - String manipulation + +class VString { + static bool wildmatchi(const char* s, const char* p); +public: + // METHODS (generic string utilities) + static bool wildmatch(const char* s, const char* p); + static string downcase(const string& str); +}; + +//###################################################################### +// Compute FNV1a (Fowler/Noll/Vo) hashes +// See http://www.isthe.com/chongo/tech/comp/fnv/index.html +// Algorithmic basis for these functions was in the public domain, by chongo + +class VHashFnv { + enum { FNV1_64_INIT = 0xcbf29ce484222325ULL }; // Initial value + vluint64_t m_hash; + + inline void hashC(uint8_t c) { + m_hash ^= c; + // Below is faster than m_hash *= 0x100000001b3ULL; + m_hash += ((m_hash << 1) + (m_hash << 4) + (m_hash << 5) + + (m_hash << 7) + (m_hash << 8) + (m_hash << 40)); + } +public: + VHashFnv() : m_hash(FNV1_64_INIT) {} + ~VHashFnv() {} + + vluint64_t value() const { return m_hash; } + + VHashFnv& hash(const void* bufp, size_t len) { // Memory + const uint8_t* bp = (const uint8_t*)bufp; + const uint8_t* be = bp + len; + while (bp < be) hashC((vluint64_t)*bp++); + return *this; + } + VHashFnv& hash(const char* strp) { // String + const uint8_t* sp = (const uint8_t*)strp; + while (*sp) hashC((vluint64_t)*sp++); + return *this; + } + VHashFnv& hash(const string& str) { return hash(str.c_str()); } + VHashFnv& hash(vluint64_t n) { + hashC(n>>0); hashC(n>>8); hashC(n>>16); hashC(n>>24); + hashC(n>>32); hashC(n>>40); hashC(n>>48); hashC(n>>56); + return *this; + } + VHashFnv& hash(uint32_t n) { + hashC(n>>0); hashC(n>>8); hashC(n>>16); hashC(n>>24); + return *this; + } + VHashFnv& hash(uint16_t n) { + hashC(n>>0); hashC(n>>8); + return *this; + } + VHashFnv& hash(uint8_t n) { hashC(n); return *this; } + VHashFnv& hash(int n) { hashC((vluint64_t)n); return *this; } +}; + +//###################################################################### + +#endif // guard diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 911b5645e..5c0623374 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -666,7 +666,7 @@ private: // We could use 64-bits of a MD5/SHA hash rather than a string here, // but the compare is only done on first call then memoized, so it's not worth optimizing. string stmt; - stmt += "static int __Vfuncnum = -1;\n"; + stmt += "static int __Vfuncnum = -1;\n"; // Static doesn't need save-restore as if below will re-fill proper value // First time init (faster than what the compiler does if we did a singleton stmt += "if (VL_UNLIKELY(__Vfuncnum==-1)) { __Vfuncnum = Verilated::exportFuncNum(\""+nodep->cname()+"\"); }\n"; // If the find fails, it will throw an error diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index 97ea7863c..70feedf0c 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -36,6 +36,7 @@ #include #include "V3Global.h" +#include "V3String.h" #include "V3Undriven.h" #include "V3Ast.h" @@ -135,7 +136,7 @@ public: bool unusedMatch(AstVar* nodep) { const char* regexpp = v3Global.opt.unusedRegexp().c_str(); if (!regexpp || !*regexpp) return false; - return V3Options::wildmatch(nodep->prettyName().c_str(), regexpp); + return VString::wildmatch(nodep->prettyName().c_str(), regexpp); } void reportViolations() { // Combine bits into overall state diff --git a/test_regress/driver.pl b/test_regress/driver.pl index 77d426f4e..2902a5140 100755 --- a/test_regress/driver.pl +++ b/test_regress/driver.pl @@ -476,6 +476,7 @@ sub compile_vlt_flags { $self->{sc} = 1 if ($checkflags =~ /-sc\b/); $self->{sp} = 1 if ($checkflags =~ /-sp\b/); $self->{trace} = 1 if ($opt_trace || $checkflags =~ /-trace\b/); + $self->{savable} = 1 if ($checkflags =~ /-savable\b/); $self->{coverage} = 1 if ($checkflags =~ /-coverage\b/); my @verilator_flags = @{$param{verilator_flags}}; @@ -963,15 +964,39 @@ sub _make_main { print $fh "#include \"systemc.h\"\n" if $self->sc; print $fh "#include \"systemperl.h\"\n" if $self->sp; print $fh "#include \"verilated_vcd_c.h\"\n" if $self->{trace} && !$self->sp; + print $fh "#include \"verilated_save.h\"\n" if $self->{savable}; print $fh "#include \"SpTraceVcd.h\"\n" if $self->{trace} && $self->sp; print $fh "$VM_PREFIX * topp;\n"; if (!$self->sc_or_sp) { - print $fh "unsigned int main_time = false;\n"; + print $fh "vluint64_t main_time = false;\n"; print $fh "double sc_time_stamp () {\n"; print $fh " return main_time;\n"; print $fh "}\n"; } + + if ($self->{savable}) { + $fh->print("\n"); + $fh->print("void save_model(const char* filenamep) {\n"); + $fh->print(" VL_PRINTF(\"Saving model to '%s'\\n\", filenamep);\n"); + $fh->print(" VerilatedSave os;\n"); + $fh->print(" os.open(filenamep);\n"); + $fh->print(" os << main_time;\n"); + $fh->print(" os << *topp;\n"); + $fh->print(" os.close();\n"); + $fh->print("}\n"); + $fh->print("\n"); + $fh->print("void restore_model(const char* filenamep) {\n"); + $fh->print(" VL_PRINTF(\"Restoring model from '%s'\\n\", filenamep);\n"); + $fh->print(" VerilatedRestore os;\n"); + $fh->print(" os.open(filenamep);\n"); + $fh->print(" os >> main_time;\n"); + $fh->print(" os >> *topp;\n"); + $fh->print(" os.close();\n"); + $fh->print("}\n"); + } + + #### Main if ($self->sc_or_sp) { print $fh "extern int sc_main(int argc, char **argv);\n"; print $fh "int sc_main(int argc, char **argv) {\n"; @@ -1017,9 +1042,24 @@ sub _make_main { $fh->print("#endif\n"); } - print $fh " ${set}fastclk = false;\n" if $self->{inputs}{fastclk}; - print $fh " ${set}clk = false;\n" if $self->{inputs}{clk}; + if ($self->{savable}) { + $fh->print(" const char* save_time_strp = Verilated::commandArgsPlusMatch(\"save_time=\");\n"); + $fh->print(" const char* save_restore_strp = Verilated::commandArgsPlusMatch(\"save_restore=\");\n"); + $fh->print(" unsigned int save_time = !save_time_strp[0] ? 0 : atoi(save_time_strp+strlen(\"+save_time=\"));\n"); + $fh->print(" unsigned int save_restore = !save_restore_strp[0] ? 0 : 1;\n"); + } + + if ($self->{savable}) { + $fh->print(" if (save_restore) {\n"); + $fh->print(" restore_model(\"$self->{obj_dir}/saved.vltsv\");\n"); + $fh->print(" } else {\n"); + } else { + $fh->print(" {\n"); + } + print $fh " ${set}fastclk = false;\n" if $self->{inputs}{fastclk}; + print $fh " ${set}clk = false;\n" if $self->{inputs}{clk}; _print_advance_time($self, $fh, 10); + print $fh " }\n"; print $fh " while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) {\n"; for (my $i=0; $i<5; $i++) { @@ -1032,6 +1072,13 @@ sub _make_main { print $fh " ${set}clk=!${set}clk;\n"; $action = 1; } + if ($self->{savable}) { + $fh->print(" if (sc_time_stamp() == save_time && save_time) {\n"); + $fh->print(" save_model(\"$self->{obj_dir}/saved.vltsv\");\n"); + $fh->print(" printf(\"Exiting after save_model\\n\");\n"); + $fh->print(" exit(0);\n"); + $fh->print(" }\n"); + } _print_advance_time($self, $fh, 1, $action); } print $fh " }\n"; diff --git a/test_regress/t/t_savable.pl b/test_regress/t/t_savable.pl new file mode 100755 index 000000000..f74dcf0fd --- /dev/null +++ b/test_regress/t/t_savable.pl @@ -0,0 +1,28 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. 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. + +compile ( + v_flags2 => ["--savable"], + save_time => 500, + ); + +execute ( + check_finished=>0, + all_run_flags => ['+save_time=500'], + ); + +-r "$Self->{obj_dir}/saved.vltsv" or $Self->error("Saved.vltsv not created\n"); + +execute ( + all_run_flags => ['+save_restore=1'], + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_savable.v b/test_regress/t/t_savable.v new file mode 100644 index 000000000..730842990 --- /dev/null +++ b/test_regress/t/t_savable.v @@ -0,0 +1,77 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2012 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + /*verilator no_inline_module*/ // So we'll get hiearachy we can test + input clk; + + sub sub (/*AUTOINST*/ + // Inputs + .clk (clk)); +endmodule + +module sub (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + /*verilator no_inline_module*/ // So we'll get hiearachy we can test + + integer cyc=0; + + reg [127:0] save128; + reg [47:0] save48; + reg [1:0] save2; + reg [255:0] cycdone; // Make sure each cycle executes exactly once + reg [31:0] vec[2:1][2:1]; + real r; + string s,s2; + + // Test loop + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d\n",$time, cyc); +`endif + cyc <= cyc + 1; + if (cycdone[cyc[7:0]]) $stop; + cycdone[cyc[7:0]] <= '1; + if (cyc==0) begin + // Setup + save128 <= 128'hc77bb9b3784ea0914afe43fb79d7b71e; + save48 <= 48'h4afe43fb79d7; + save2 <= 2'b10; + vec[1][1] <= 32'h0101; + vec[1][2] <= 32'h0102; + vec[2][1] <= 32'h0201; + vec[2][2] <= 32'h0202; + r <= 1.234; + s <= "hello"; + end + if (cyc==1) begin + if ($test$plusargs("save_restore")!=0) begin + // Don't allow the restored model to run from time 0, it must run from a restore + $write("%%Error: didn't really restore\n"); + $stop; + end + end + else if (cyc==99) begin + if (save128 !== 128'hc77bb9b3784ea0914afe43fb79d7b71e) $stop; + if (save48 !== 48'h4afe43fb79d7) $stop; + if (save2 !== 2'b10) $stop; + if (cycdone !== {{(256-99){1'b0}}, {99{1'b1}}}) $stop; + if (vec[1][1] !== 32'h0101) $stop; + if (vec[1][2] !== 32'h0102) $stop; + if (vec[2][1] !== 32'h0201) $stop; + if (vec[2][2] !== 32'h0202) $stop; + if (r != 1.234) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule