mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
d4f7f5297a
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".
504 lines
18 KiB
C++
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
|