forked from github/verilator
332 lines
12 KiB
C++
332 lines
12 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//=============================================================================
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
// Copyright 2000-2023 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.
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
//
|
|
//=============================================================================
|
|
///
|
|
/// \file
|
|
/// \brief Verilated save-restore serialization header
|
|
///
|
|
/// This must be included in user wrapper code that wants to use
|
|
/// save/restore.
|
|
///
|
|
//=============================================================================
|
|
|
|
#ifndef VERILATOR_VERILATED_SAVE_C_H_
|
|
#define VERILATOR_VERILATED_SAVE_C_H_
|
|
|
|
#include "verilatedos.h"
|
|
|
|
#include "verilated.h"
|
|
|
|
#include <string>
|
|
|
|
//=============================================================================
|
|
// VerilatedSerialize
|
|
/// Class for writing serialization of structures to a stream representation.
|
|
///
|
|
/// User wrapper code will more typically use VerilatedSave which uses this
|
|
/// as a subclass to write a file.
|
|
///
|
|
/// This class is not thread safe, it must be called by a single thread
|
|
|
|
class VerilatedSerialize VL_NOT_FINAL {
|
|
protected:
|
|
// MEMBERS
|
|
// For speed, keep m_cp as the first member of this structure
|
|
uint8_t* m_cp; // Current pointer into m_bufp buffer
|
|
uint8_t* m_bufp; // Output buffer
|
|
bool m_isOpen = false; // True indicates open file/stream
|
|
std::string m_filename; // Filename, for error messages
|
|
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
|
|
|
|
static constexpr size_t bufferSize() { return 256 * 1024; } // See below for slack calculation
|
|
static constexpr size_t bufferInsertSize() { return 16 * 1024; }
|
|
|
|
void header() VL_MT_UNSAFE_ONE;
|
|
void trailer() VL_MT_UNSAFE_ONE;
|
|
|
|
// CONSTRUCTORS
|
|
VL_UNCOPYABLE(VerilatedSerialize);
|
|
|
|
public:
|
|
/// Construct
|
|
VerilatedSerialize() {
|
|
m_bufp = new uint8_t[bufferSize()];
|
|
m_cp = m_bufp;
|
|
}
|
|
/// Flush, close, and destruct
|
|
virtual ~VerilatedSerialize() {
|
|
// Child classes will need to typically call closeImp() in destructors
|
|
if (m_bufp) VL_DO_CLEAR(delete[] m_bufp, m_bufp = nullptr);
|
|
}
|
|
// METHODS
|
|
/// Return true if file is open
|
|
bool isOpen() const { return m_isOpen; }
|
|
/// Return current filename
|
|
std::string filename() const { return m_filename; }
|
|
/// Close the stream
|
|
virtual void close() VL_MT_UNSAFE_ONE { flush(); }
|
|
/// Flush pending data to stream
|
|
virtual void flush() VL_MT_UNSAFE_ONE {}
|
|
/// Write data to stream
|
|
VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
|
const uint8_t* __restrict dp = static_cast<const uint8_t* __restrict>(datap);
|
|
while (size) {
|
|
bufferCheck();
|
|
size_t blk = size;
|
|
if (blk > bufferInsertSize()) blk = bufferInsertSize();
|
|
const uint8_t* __restrict maxp = dp + blk;
|
|
for (; dp < maxp; *m_cp++ = *dp++) {}
|
|
size -= blk;
|
|
}
|
|
return *this; // For function chaining
|
|
}
|
|
|
|
private:
|
|
VerilatedSerialize& bufferCheck() VL_MT_UNSAFE_ONE {
|
|
// 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
|
|
}
|
|
};
|
|
|
|
//=============================================================================
|
|
// VerilatedDeserialize
|
|
/// Class for loading structures from a stream representation.
|
|
///
|
|
/// User wrapper code will more typically use VerilatedRestore which uses
|
|
/// this as a subclass to a read from a file.
|
|
///
|
|
/// This class is not thread safe, it must be called by a single thread
|
|
|
|
class VerilatedDeserialize VL_NOT_FINAL {
|
|
protected:
|
|
// MEMBERS
|
|
// For speed, keep m_cp as the first member of this structure
|
|
uint8_t* m_cp; // Current pointer into m_bufp buffer
|
|
uint8_t* m_bufp; // Output buffer
|
|
uint8_t* m_endp = nullptr; // Last valid byte in m_bufp buffer
|
|
bool m_isOpen = false; // True indicates open file/stream
|
|
std::string m_filename; // Filename, for error messages
|
|
VerilatedAssertOneThread m_assertOne; // Assert only called from single thread
|
|
|
|
static constexpr size_t bufferSize() { return 256 * 1024; } // See below for slack calculation
|
|
static constexpr size_t bufferInsertSize() { return 16 * 1024; }
|
|
|
|
virtual void fill() = 0;
|
|
void header() VL_MT_UNSAFE_ONE;
|
|
void trailer() VL_MT_UNSAFE_ONE;
|
|
|
|
// CONSTRUCTORS
|
|
VL_UNCOPYABLE(VerilatedDeserialize);
|
|
|
|
public:
|
|
/// Construct
|
|
VerilatedDeserialize() {
|
|
m_bufp = new uint8_t[bufferSize()];
|
|
m_cp = m_bufp;
|
|
}
|
|
/// Destruct
|
|
virtual ~VerilatedDeserialize() {
|
|
// Child classes will need to typically call closeImp() in destructors
|
|
if (m_bufp) VL_DO_CLEAR(delete[] m_bufp, m_bufp = nullptr);
|
|
}
|
|
// METHODS
|
|
/// Return true if file is open
|
|
bool isOpen() const { return m_isOpen; }
|
|
/// Return current filename
|
|
std::string filename() const { return m_filename; }
|
|
/// Close the stream
|
|
virtual void close() VL_MT_UNSAFE_ONE { flush(); }
|
|
/// Flush pending data to stream
|
|
virtual void flush() VL_MT_UNSAFE_ONE {}
|
|
/// Read data from stream
|
|
VerilatedDeserialize& read(void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
|
|
uint8_t* __restrict dp = static_cast<uint8_t* __restrict>(datap);
|
|
while (size) {
|
|
bufferCheck();
|
|
size_t blk = size;
|
|
if (blk > bufferInsertSize()) blk = bufferInsertSize();
|
|
const uint8_t* __restrict maxp = dp + blk;
|
|
for (; dp < maxp; *dp++ = *m_cp++) {}
|
|
size -= blk;
|
|
}
|
|
return *this; // For function chaining
|
|
}
|
|
|
|
// Internal use:
|
|
// Read a datum and compare with expected value
|
|
VerilatedDeserialize& readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
|
|
VerilatedDeserialize& readAssert(uint64_t data) VL_MT_UNSAFE_ONE {
|
|
return readAssert(&data, sizeof(data));
|
|
}
|
|
|
|
private:
|
|
bool readDiffers(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
|
|
VerilatedDeserialize& bufferCheck() VL_MT_UNSAFE_ONE {
|
|
// 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
|
|
/// Stream-like object that serializes Verilated model to a file.
|
|
///
|
|
/// This class is not thread safe, it must be called by a single thread
|
|
|
|
class VerilatedSave final : public VerilatedSerialize {
|
|
private:
|
|
int m_fd = -1; // File descriptor we're writing to
|
|
|
|
void closeImp() VL_MT_UNSAFE_ONE;
|
|
void flushImp() VL_MT_UNSAFE_ONE;
|
|
|
|
public:
|
|
// CONSTRUCTORS
|
|
/// Construct new object
|
|
VerilatedSave() = default;
|
|
/// Flush, close and destruct
|
|
~VerilatedSave() override { closeImp(); }
|
|
// METHODS
|
|
/// Open the file; call isOpen() to see if errors
|
|
void open(const char* filenamep) VL_MT_UNSAFE_ONE;
|
|
/// Open the file; call isOpen() to see if errors
|
|
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
|
|
/// Flush and close the file
|
|
void close() override VL_MT_UNSAFE_ONE { closeImp(); }
|
|
/// Flush data to file
|
|
void flush() override VL_MT_UNSAFE_ONE { flushImp(); }
|
|
};
|
|
|
|
//=============================================================================
|
|
// VerilatedRestore
|
|
/// Stream-like object that serializes Verilated model from a file.
|
|
///
|
|
/// This class is not thread safe, it must be called by a single thread
|
|
|
|
class VerilatedRestore final : public VerilatedDeserialize {
|
|
private:
|
|
int m_fd = -1; // File descriptor we're writing to
|
|
|
|
void closeImp() VL_MT_UNSAFE_ONE;
|
|
void flushImp() VL_MT_UNSAFE_ONE {}
|
|
|
|
public:
|
|
// CONSTRUCTORS
|
|
/// Construct new object
|
|
VerilatedRestore() = default;
|
|
/// Flush, close and destruct
|
|
~VerilatedRestore() override { closeImp(); }
|
|
|
|
// METHODS
|
|
/// Open the file; call isOpen() to see if errors
|
|
void open(const char* filenamep) VL_MT_UNSAFE_ONE;
|
|
/// Open the file; call isOpen() to see if errors
|
|
void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
|
|
/// Close the file
|
|
void close() override VL_MT_UNSAFE_ONE { closeImp(); }
|
|
void flush() override VL_MT_UNSAFE_ONE { flushImp(); }
|
|
void fill() override VL_MT_UNSAFE_ONE;
|
|
};
|
|
|
|
//=============================================================================
|
|
|
|
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint64_t& rhs) {
|
|
return os.write(&rhs, sizeof(rhs));
|
|
}
|
|
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint64_t& rhs) {
|
|
return os.read(&rhs, sizeof(rhs));
|
|
}
|
|
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint32_t& rhs) {
|
|
return os.write(&rhs, sizeof(rhs));
|
|
}
|
|
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint32_t& rhs) {
|
|
return os.read(&rhs, sizeof(rhs));
|
|
}
|
|
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint16_t& rhs) {
|
|
return os.write(&rhs, sizeof(rhs));
|
|
}
|
|
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint16_t& rhs) {
|
|
return os.read(&rhs, sizeof(rhs));
|
|
}
|
|
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint8_t& rhs) {
|
|
return os.write(&rhs, sizeof(rhs));
|
|
}
|
|
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint8_t& rhs) {
|
|
return os.read(&rhs, sizeof(rhs));
|
|
}
|
|
inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const 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, const 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, const 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, const std::string& rhs) {
|
|
const uint32_t len = rhs.length();
|
|
os << len;
|
|
return os.write(rhs.data(), len);
|
|
}
|
|
inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& rhs) {
|
|
uint32_t len = 0;
|
|
os >> len;
|
|
rhs.resize(len);
|
|
// C cast is required below
|
|
return os.read((void*)(rhs.data()), len);
|
|
}
|
|
VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp);
|
|
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp);
|
|
|
|
template <class T_Key, class T_Value>
|
|
VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
|
|
os << rhs.atDefault();
|
|
const uint32_t len = rhs.size();
|
|
os << len;
|
|
for (const auto& i : rhs) {
|
|
const T_Key index = i.first; // Copy to get around const_iterator
|
|
const T_Value value = i.second;
|
|
os << index << value;
|
|
}
|
|
return os;
|
|
}
|
|
template <class T_Key, class T_Value>
|
|
VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
|
|
os >> rhs.atDefault();
|
|
uint32_t len = 0;
|
|
os >> len;
|
|
rhs.clear();
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
T_Key index;
|
|
T_Value value;
|
|
os >> index;
|
|
os >> value;
|
|
rhs.at(index) = value;
|
|
}
|
|
return os;
|
|
}
|
|
|
|
#endif // Guard
|