verilator/include/verilated_heavy.h
Wilson Snyder d4f7f5297a
Support IEEE time units and time precisions, #234. (#2253)
Includes `timescale, $printtimescale, $timeformat.
VL_TIME_MULTIPLIER, VL_TIME_PRECISION, VL_TIME_UNIT have been removed
and the time precision must now match the SystemC time precision.
To get closer behavior to older versions, use e.g. --timescale-override
"1ps/1ps".
2020-04-15 19:39:03 -04:00

504 lines
18 KiB
C++

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
//
// Copyright 2010-2020 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 Verilator: String include for all Verilated C files
///
/// This file is included automatically by Verilator at the top of
/// all C++ files it generates. It is used when strings or other
/// heavyweight types are required; these contents are not part of
/// verilated.h to save compile time when such types aren't used.
///
/// Code available from: https://verilator.org
///
//*************************************************************************
#ifndef _VERILATED_HEAVY_H_
#define _VERILATED_HEAVY_H_ 1 ///< Header Guard
#include "verilated.h"
#include <deque>
#include <map>
#include <memory>
#include <string>
//===================================================================
// String formatters (required by below containers)
extern std::string VL_TO_STRING(CData lhs);
extern std::string VL_TO_STRING(SData lhs);
extern std::string VL_TO_STRING(IData lhs);
extern std::string VL_TO_STRING(QData lhs);
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
extern std::string VL_TO_STRING_W(int words, WDataInP obj);
//===================================================================
// Readmem/Writemem operation classes
class VlReadMem {
bool m_hex; // Hex format
int m_bits; // Bit width of values
const std::string& m_filename; // Filename
QData m_end; // End address (as specified by user)
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to read
int m_linenum; // Line number last read from file
public:
VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlReadMem();
bool isOpen() const { return m_fp != NULL; }
int linenum() const { return m_linenum; }
bool get(QData& addrr, std::string& valuer);
void setData(void* valuep, const std::string& rhs);
};
class VlWriteMem {
int m_bits; // Bit width of values
FILE* m_fp; // File handle for filename
QData m_addr; // Next address to write
public:
VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
~VlWriteMem();
bool isOpen() const { return m_fp != NULL; }
void print(QData addr, bool addrstamp, const void* valuep);
};
//===================================================================
// Verilog array container
// Similar to std::array<WData, N>, but:
// 1. Doesn't require C++11
// 2. Lighter weight, only methods needed by Verilator, to help compile time.
//
// This is only used when we need an upper-level container and so can't
// simply use a C style array (which is just a pointer).
template <std::size_t T_Words> class VlWide {
WData m_storage[T_Words];
public:
// cppcheck-suppress uninitVar
VlWide() {}
~VlWide() {}
const WData& at(size_t index) const { return m_storage[index]; }
WData& at(size_t index) { return m_storage[index]; }
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
bool operator<(const VlWide<T_Words>& rhs) const {
return VL_LT_W(T_Words, data(), rhs.data());
}
};
// Convert a C array to std::array reference by pointer magic, without copy.
// Data type (second argument) is so the function template can automatically generate.
template <std::size_t T_Words> VlWide<T_Words>& VL_CVT_W_A(WDataInP inp, const VlWide<T_Words>&) {
return *((VlWide<T_Words>*)inp);
}
template <std::size_t T_Words> std::string VL_TO_STRING(const VlWide<T_Words>& obj) {
return VL_TO_STRING_W(T_Words, obj.data());
}
//===================================================================
// Verilog associative array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
template <class T_Key, class T_Value> class VlAssocArray {
private:
// TYPES
typedef std::map<T_Key, T_Value> Map;
public:
typedef typename Map::const_iterator const_iterator;
private:
// MEMBERS
Map m_map; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
VlAssocArray() {
// m_defaultValue isn't defaulted. Caller's constructor must do it.
}
~VlAssocArray() {}
// Standard copy constructor works. Verilog: assoca = assocb
// METHODS
T_Value& atDefault() { return m_defaultValue; }
// Size of array. Verilog: function int size(), or int num()
int size() const { return m_map.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_map.clear(); }
void erase(const T_Key& index) { m_map.erase(index); }
// Return 0/1 if element exists. Verilog: function int exists(input index)
int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); }
// Return first element. Verilog: function int first(ref index);
int first(T_Key& indexr) const {
typename Map::const_iterator it = m_map.begin();
if (it == m_map.end()) return 0;
indexr = it->first;
return 1;
}
// Return last element. Verilog: function int last(ref index)
int last(T_Key& indexr) const {
typename Map::const_reverse_iterator it = m_map.rbegin();
if (it == m_map.rend()) return 0;
indexr = it->first;
return 1;
}
// Return next element. Verilog: function int next(ref index)
int next(T_Key& indexr) const {
typename Map::const_iterator it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
++it;
if (VL_UNLIKELY(it == m_map.end())) return 0;
indexr = it->first;
return 1;
}
// Return prev element. Verilog: function int prev(ref index)
int prev(T_Key& indexr) const {
typename Map::const_iterator it = m_map.find(indexr);
if (VL_UNLIKELY(it == m_map.end())) return 0;
if (VL_UNLIKELY(it == m_map.begin())) return 0;
--it;
indexr = it->first;
return 1;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(const T_Key& index) {
typename Map::iterator it = m_map.find(index);
if (it == m_map.end()) {
std::pair<typename Map::iterator, bool> pit
= m_map.insert(std::make_pair(index, m_defaultValue));
return pit.first->second;
}
return it->second;
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(const T_Key& index) const {
typename Map::iterator it = m_map.find(index);
if (it == m_map.end()) {
return m_defaultValue;
} else {
return it->second;
}
}
// For save/restore
const_iterator begin() const { return m_map.begin(); }
const_iterator end() const { return m_map.end(); }
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
std::string out = "'{";
std::string comma;
for (typename Map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) {
out += comma + VL_TO_STRING(it->first) + ":" + VL_TO_STRING(it->second);
comma = ", ";
}
// Default not printed - maybe random init data
return out + "} ";
}
};
template <class T_Key, class T_Value>
std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
return obj.to_string();
}
template <class T_Key, class T_Value>
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlReadMem rmem(hex, bits, filename, start, end);
if (VL_UNLIKELY(!rmem.isOpen())) return;
while (true) {
QData addr;
std::string data;
if (rmem.get(addr /*ref*/, data /*ref*/)) {
rmem.setData(&(obj.at(addr)), data);
} else {
break;
}
}
}
template <class T_Key, class T_Value>
void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
const VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
VlWriteMem wmem(hex, bits, filename, start, end);
if (VL_UNLIKELY(!wmem.isOpen())) return;
for (typename VlAssocArray<T_Key, T_Value>::const_iterator it = obj.begin(); it != obj.end();
++it) {
QData addr = it->first;
if (addr >= start && addr <= end) wmem.print(addr, true, &(it->second));
}
}
//===================================================================
// Verilog queue and dynamic array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
// For dynamic arrays it is always zero
template <class T_Value, size_t T_MaxSize = 0> class VlQueue {
private:
// TYPES
typedef std::deque<T_Value> Deque;
public:
typedef typename Deque::const_iterator const_iterator;
private:
// MEMBERS
Deque m_deque; // State of the assoc array
T_Value m_defaultValue; // Default value
public:
// CONSTRUCTORS
VlQueue() {
// m_defaultValue isn't defaulted. Caller's constructor must do it.
}
~VlQueue() {}
// Standard copy constructor works. Verilog: assoca = assocb
// METHODS
T_Value& atDefault() { return m_defaultValue; }
// Size. Verilog: function int size(), or int num()
int size() const { return m_deque.size(); }
// Clear array. Verilog: function void delete([input index])
void clear() { m_deque.clear(); }
void erase(size_t index) {
if (VL_LIKELY(index < m_deque.size())) m_deque.erase(index);
}
// Dynamic array new[] becomes a renew()
void renew(size_t size) {
clear();
m_deque.resize(size, atDefault());
}
// Dynamic array new[]() becomes a renew_copy()
void renew_copy(size_t size, const VlQueue<T_Value, T_MaxSize>& rhs) {
if (size == 0) {
clear();
} else {
*this = rhs;
m_deque.resize(size, atDefault());
}
}
// function void q.push_front(value)
void push_front(const T_Value& value) {
m_deque.push_front(value);
if (VL_UNLIKELY(T_MaxSize != 0 && m_deque.size() > T_MaxSize)) m_deque.pop_back();
}
// function void q.push_back(value)
void push_back(const T_Value& value) {
if (VL_LIKELY(T_MaxSize == 0 || m_deque.size() < T_MaxSize)) m_deque.push_back(value);
}
// function value_t q.pop_front();
T_Value pop_front() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.front();
m_deque.pop_front();
return v;
}
// function value_t q.pop_back();
T_Value pop_back() {
if (m_deque.empty()) return m_defaultValue;
T_Value v = m_deque.back();
m_deque.pop_back();
return v;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(size_t index) {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index >= m_deque.size())) {
s_throwAway = atDefault();
return s_throwAway;
} else {
return m_deque[index];
}
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(size_t index) const {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index >= m_deque.size())) {
return atDefault();
} else {
return m_deque[index];
}
}
// function void q.insert(index, value);
void insert(size_t index, const T_Value& value) {
if (VL_UNLIKELY(index >= m_deque.size())) return;
m_deque[index] = value;
}
// For save/restore
const_iterator begin() const { return m_deque.begin(); }
const_iterator end() const { return m_deque.end(); }
// Dumping. Verilog: str = $sformatf("%p", assoc)
std::string to_string() const {
std::string out = "'{";
std::string comma;
for (typename Deque::const_iterator it = m_deque.begin(); it != m_deque.end(); ++it) {
out += comma + VL_TO_STRING(*it);
comma = ", ";
}
return out + "} ";
}
};
template <class T_Value> std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
return obj.to_string();
}
//===================================================================
// Verilog class reference container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
// clang-format off
#if (defined(_MSC_VER) && _MSC_VER >= 1900) || (__cplusplus >= 201103L)
# define VlClassRef std::shared_ptr
#else
# define VlClassRef VlClassRef__SystemVerilog_class_support_requires_a_C11_or_newer_compiler
#endif
// clang-format on
template <class T> // T typically of type VlClassRef<x>
inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
return t;
}
//======================================================================
// Conversion functions
extern std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE;
inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
WData lw[VL_WQ_WORDS_E];
VL_SET_WQ(lw, lhs);
return VL_CVT_PACK_STR_NW(VL_WQ_WORDS_E, lw);
}
inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE { return lhs; }
inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE {
WData lw[VL_WQ_WORDS_E];
VL_SET_WI(lw, lhs);
return VL_CVT_PACK_STR_NW(1, lw);
}
inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE {
return lhs + rhs;
}
inline std::string VL_REPLICATEN_NNQ(int, int, int, const std::string& lhs, IData rep) VL_PURE {
std::string out;
out.reserve(lhs.length() * rep);
for (unsigned times = 0; times < rep; ++times) out += lhs;
return out;
}
inline std::string VL_REPLICATEN_NNI(int obits, int lbits, int rbits, const std::string& lhs,
IData rep) VL_PURE {
return VL_REPLICATEN_NNQ(obits, lbits, rbits, lhs, rep);
}
inline IData VL_LEN_IN(const std::string& ld) { return ld.length(); }
extern std::string VL_TOLOWER_NN(const std::string& ld);
extern std::string VL_TOUPPER_NN(const std::string& ld);
extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE;
extern IData VL_FOPEN_NI(const std::string& filename, IData mode) VL_MT_SAFE;
extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb,
const std::string& filename, void* memp, QData start,
QData end) VL_MT_SAFE;
extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb,
const std::string& filename, const void* memp, QData start,
QData end) VL_MT_SAFE;
extern IData VL_SSCANF_INX(int lbits, const std::string& ld, const char* formatp, ...) VL_MT_SAFE;
extern void VL_SFORMAT_X(int obits_ignored, std::string& output, const char* formatp,
...) VL_MT_SAFE;
extern std::string VL_SFORMATF_NX(const char* formatp, ...) VL_MT_SAFE;
extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix,
int width) VL_MT_SAFE;
extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE;
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE {
WData rwp[2]; // WData must always be at least 2
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = rwp[0];
return got;
}
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, SData& rdr) VL_MT_SAFE {
WData rwp[2]; // WData must always be at least 2
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = rwp[0];
return got;
}
inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE {
WData rwp[2];
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = rwp[0];
return got;
}
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) VL_MT_SAFE {
WData rwp[2];
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = VL_SET_QW(rwp);
return got;
}
inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE {
WData rwp[2];
IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp));
return got;
}
extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE;
//======================================================================
// Strings
extern std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE;
extern CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE;
extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE;
inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE {
// SystemVerilog does not allow a string variable to contain '\0'.
// So C functions such as strcmp() can correctly compare strings.
int result;
if (ignoreCase) {
result = VL_STRCASECMP(lhs.c_str(), rhs.c_str());
} else {
result = std::strcmp(lhs.c_str(), rhs.c_str());
}
return result;
}
extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE;
//======================================================================
// Dumping
extern const char* vl_dumpctl_filenamep(bool setit = false,
const std::string& filename = "") VL_MT_SAFE;
#endif // Guard