Support associative arrays.
This commit is contained in:
parent
6ae7f7b152
commit
b81295230a
2
Changes
2
Changes
|
|
@ -4,6 +4,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
|
||||||
|
|
||||||
* Verilator 4.023 devel
|
* Verilator 4.023 devel
|
||||||
|
|
||||||
|
** Support associative arrays (excluding [*] and pattern assignments).
|
||||||
|
|
||||||
*** Add +verilator+error+limit to see more assertion errors. [Peter Monsson]
|
*** Add +verilator+error+limit to see more assertion errors. [Peter Monsson]
|
||||||
|
|
||||||
*** Support string.toupper and string.tolower.
|
*** Support string.toupper and string.tolower.
|
||||||
|
|
|
||||||
|
|
@ -1758,6 +1758,19 @@ const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE {
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// Heavy string functions
|
// Heavy string functions
|
||||||
|
|
||||||
|
std::string VL_TO_STRING(CData lhs) {
|
||||||
|
return VL_SFORMATF_NX("'h%0x", 8, lhs);
|
||||||
|
}
|
||||||
|
std::string VL_TO_STRING(SData lhs) {
|
||||||
|
return VL_SFORMATF_NX("'h%0x", 16, lhs);
|
||||||
|
}
|
||||||
|
std::string VL_TO_STRING(IData lhs) {
|
||||||
|
return VL_SFORMATF_NX("'h%0x", 32, lhs);
|
||||||
|
}
|
||||||
|
std::string VL_TO_STRING(QData lhs) {
|
||||||
|
return VL_SFORMATF_NX("'h%0x", 64, lhs);
|
||||||
|
}
|
||||||
|
|
||||||
std::string VL_TOLOWER_NN(const std::string& ld) VL_MT_SAFE {
|
std::string VL_TOLOWER_NN(const std::string& ld) VL_MT_SAFE {
|
||||||
std::string out = ld;
|
std::string out = ld;
|
||||||
for (std::string::iterator it = out.begin(); it != out.end(); ++it) {
|
for (std::string::iterator it = out.begin(); it != out.end(); ++it) {
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,158 @@
|
||||||
|
|
||||||
#include "verilated.h"
|
#include "verilated.h"
|
||||||
|
|
||||||
|
//IFDEF C11
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// String formatters (required by below containers)
|
||||||
|
|
||||||
|
extern std::string VL_TO_STRING(CData obj);
|
||||||
|
extern std::string VL_TO_STRING(SData obj);
|
||||||
|
extern std::string VL_TO_STRING(IData obj);
|
||||||
|
extern std::string VL_TO_STRING(QData obj);
|
||||||
|
inline std::string VL_TO_STRING(const std::string& obj) { return "\""+obj+"\""; }
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// 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:
|
||||||
|
// Default constructor/destructor/copy are fine
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
//======================================================================
|
//======================================================================
|
||||||
// Conversion functions
|
// Conversion functions
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#define _VERILATED_SAVE_C_H_ 1
|
#define _VERILATED_SAVE_C_H_ 1
|
||||||
|
|
||||||
#include "verilatedos.h"
|
#include "verilatedos.h"
|
||||||
|
#include "verilated_heavy.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
|
@ -247,5 +248,33 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& r
|
||||||
rhs.resize(len);
|
rhs.resize(len);
|
||||||
return os.read((void*)rhs.data(), len);
|
return os.read((void*)rhs.data(), len);
|
||||||
}
|
}
|
||||||
|
template <class T_Key, class T_Value>
|
||||||
|
VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray<T_Key, T_Value>& rhs) {
|
||||||
|
os << rhs.atDefault();
|
||||||
|
vluint32_t len = rhs.size();
|
||||||
|
os << len;
|
||||||
|
for (typename VlAssocArray<T_Key, T_Value>::const_iterator it = rhs.begin();
|
||||||
|
it != rhs.end(); ++it) {
|
||||||
|
T_Key index = it->first; // Copy to get around const_iterator
|
||||||
|
T_Value value = it->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();
|
||||||
|
vluint32_t len = 0;
|
||||||
|
os >> len;
|
||||||
|
rhs.clear();
|
||||||
|
for (vluint32_t i = 0; i < len; ++i) {
|
||||||
|
T_Key index;
|
||||||
|
T_Value value;
|
||||||
|
os >> index;
|
||||||
|
os >> value;
|
||||||
|
rhs.at(index) = value;
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
|
|
||||||
|
|
@ -1199,6 +1199,10 @@ AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) {
|
||||||
return v3Global.rootp()->typeTablep()
|
return v3Global.rootp()->typeTablep()
|
||||||
->findInsertSameDType(nodep);
|
->findInsertSameDType(nodep);
|
||||||
}
|
}
|
||||||
|
AstNodeDType* AstNode::findVoidDType() const {
|
||||||
|
return v3Global.rootp()->typeTablep()
|
||||||
|
->findVoidDType(fileline());
|
||||||
|
}
|
||||||
|
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// AstNVisitor
|
// AstNVisitor
|
||||||
|
|
|
||||||
|
|
@ -1421,6 +1421,7 @@ public:
|
||||||
void dtypeSetSigned32() { dtypep(findSigned32DType()); }
|
void dtypeSetSigned32() { dtypep(findSigned32DType()); }
|
||||||
void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate
|
void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate
|
||||||
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
|
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
|
||||||
|
void dtypeSetVoid() { dtypep(findVoidDType()); }
|
||||||
|
|
||||||
// Data type locators
|
// Data type locators
|
||||||
AstNodeDType* findLogicBoolDType() { return findBasicDType(AstBasicDTypeKwd::LOGIC); }
|
AstNodeDType* findLogicBoolDType() { return findBasicDType(AstBasicDTypeKwd::LOGIC); }
|
||||||
|
|
@ -1429,6 +1430,7 @@ public:
|
||||||
AstNodeDType* findSigned32DType() { return findBasicDType(AstBasicDTypeKwd::INTEGER); }
|
AstNodeDType* findSigned32DType() { return findBasicDType(AstBasicDTypeKwd::INTEGER); }
|
||||||
AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); } // Twostate
|
AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); } // Twostate
|
||||||
AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); } // Twostate
|
AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); } // Twostate
|
||||||
|
AstNodeDType* findVoidDType() const;
|
||||||
AstNodeDType* findBitDType(int width, int widthMin, AstNumeric numeric) const;
|
AstNodeDType* findBitDType(int width, int widthMin, AstNumeric numeric) const;
|
||||||
AstNodeDType* findLogicDType(int width, int widthMin, AstNumeric numeric) const;
|
AstNodeDType* findLogicDType(int width, int widthMin, AstNumeric numeric) const;
|
||||||
AstNodeDType* findLogicRangeDType(VNumRange range, int widthMin, AstNumeric numeric) const;
|
AstNodeDType* findLogicRangeDType(VNumRange range, int widthMin, AstNumeric numeric) const;
|
||||||
|
|
@ -1914,6 +1916,8 @@ public:
|
||||||
virtual bool maybePointedTo() const { return true; }
|
virtual bool maybePointedTo() const { return true; }
|
||||||
virtual AstNodeDType* virtRefDTypep() const { return NULL; } // Iff has a non-null refDTypep(), as generic node function
|
virtual AstNodeDType* virtRefDTypep() const { return NULL; } // Iff has a non-null refDTypep(), as generic node function
|
||||||
virtual void virtRefDTypep(AstNodeDType* nodep) { } // Iff has refDTypep(), set as generic node function
|
virtual void virtRefDTypep(AstNodeDType* nodep) { } // Iff has refDTypep(), set as generic node function
|
||||||
|
virtual AstNodeDType* virtRefDType2p() const { return NULL; } // Iff has a non-null second dtypep, as generic node function
|
||||||
|
virtual void virtRefDType2p(AstNodeDType* nodep) { } // Iff has second dtype, set as generic node function
|
||||||
virtual bool similarDType(AstNodeDType* samep) const = 0; // Assignable equivalence. Call skipRefp() on this and samep before calling
|
virtual bool similarDType(AstNodeDType* samep) const = 0; // Assignable equivalence. Call skipRefp() on this and samep before calling
|
||||||
virtual AstNodeDType* subDTypep() const { return NULL; } // Iff has a non-null subDTypep(), as generic node function
|
virtual AstNodeDType* subDTypep() const { return NULL; } // Iff has a non-null subDTypep(), as generic node function
|
||||||
virtual bool isFourstate() const;
|
virtual bool isFourstate() const;
|
||||||
|
|
|
||||||
|
|
@ -247,7 +247,7 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
|
||||||
string ostatic;
|
string ostatic;
|
||||||
if (isStatic() && namespc.empty()) ostatic = "static ";
|
if (isStatic() && namespc.empty()) ostatic = "static ";
|
||||||
|
|
||||||
VlArgTypeRecursed info = vlArgTypeRecurse(forFunc, dtypep());
|
VlArgTypeRecursed info = vlArgTypeRecurse(forFunc, dtypep(), false);
|
||||||
|
|
||||||
string oname;
|
string oname;
|
||||||
if (named) {
|
if (named) {
|
||||||
|
|
@ -258,11 +258,28 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
|
||||||
return ostatic + info.m_oprefix + info.refParen(oname) + info.m_osuffix;
|
return ostatic + info.m_oprefix + info.refParen(oname) + info.m_osuffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstVar::VlArgTypeRecursed AstVar::vlArgTypeRecurse(bool forFunc,
|
AstVar::VlArgTypeRecursed AstVar::vlArgTypeRecurse(bool forFunc, const AstNodeDType* dtypep,
|
||||||
const AstNodeDType* dtypep) const {
|
bool arrayed) const {
|
||||||
dtypep = dtypep->skipRefp();
|
dtypep = dtypep->skipRefp();
|
||||||
if (const AstUnpackArrayDType* adtypep = VN_CAST_CONST(dtypep, UnpackArrayDType)) {
|
if (const AstAssocArrayDType* adtypep = VN_CAST_CONST(dtypep, AssocArrayDType)) {
|
||||||
VlArgTypeRecursed info = vlArgTypeRecurse(forFunc, adtypep->subDTypep());
|
VlArgTypeRecursed key = vlArgTypeRecurse(forFunc, adtypep->keyDTypep(), true);
|
||||||
|
VlArgTypeRecursed sub = vlArgTypeRecurse(forFunc, adtypep->subDTypep(), true);
|
||||||
|
string out = "VlAssocArray<";
|
||||||
|
out += key.m_oprefix;
|
||||||
|
if (!key.m_osuffix.empty() || !key.m_oref.empty()) {
|
||||||
|
out += " " + key.m_osuffix + key.m_oref;
|
||||||
|
}
|
||||||
|
out += ", ";
|
||||||
|
out += sub.m_oprefix;
|
||||||
|
if (!sub.m_osuffix.empty() || !sub.m_oref.empty()) {
|
||||||
|
out += " " + sub.m_osuffix + sub.m_oref;
|
||||||
|
}
|
||||||
|
out += ">";
|
||||||
|
VlArgTypeRecursed info;
|
||||||
|
info.m_oprefix = out;
|
||||||
|
return info;
|
||||||
|
} else if (const AstUnpackArrayDType* adtypep = VN_CAST_CONST(dtypep, UnpackArrayDType)) {
|
||||||
|
VlArgTypeRecursed info = vlArgTypeRecurse(forFunc, adtypep->subDTypep(), arrayed);
|
||||||
info.m_osuffix = "[" + cvtToStr(adtypep->declRange().elements()) + "]" + info.m_osuffix;
|
info.m_osuffix = "[" + cvtToStr(adtypep->declRange().elements()) + "]" + info.m_osuffix;
|
||||||
return info;
|
return info;
|
||||||
} else if (const AstBasicDType* bdtypep = dtypep->basicp()) {
|
} else if (const AstBasicDType* bdtypep = dtypep->basicp()) {
|
||||||
|
|
@ -297,8 +314,12 @@ AstVar::VlArgTypeRecursed AstVar::vlArgTypeRecurse(bool forFunc,
|
||||||
} else if (dtypep->isQuad()) {
|
} else if (dtypep->isQuad()) {
|
||||||
otype += "QData"+bitvec;
|
otype += "QData"+bitvec;
|
||||||
} else if (dtypep->isWide()) {
|
} else if (dtypep->isWide()) {
|
||||||
otype += "WData"+bitvec; // []'s added later
|
if (arrayed) {
|
||||||
oarray += "["+cvtToStr(dtypep->widthWords())+"]";
|
otype += "VlWide<"+cvtToStr(dtypep->widthWords())+">";
|
||||||
|
} else {
|
||||||
|
otype += "WData"+bitvec; // []'s added later
|
||||||
|
oarray += "["+cvtToStr(dtypep->widthWords())+"]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string oref;
|
string oref;
|
||||||
|
|
@ -740,6 +761,15 @@ void AstTypeTable::repairCache() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AstVoidDType* AstTypeTable::findVoidDType(FileLine* fl) {
|
||||||
|
if (VL_UNLIKELY(!m_voidp)) {
|
||||||
|
AstVoidDType* newp = new AstVoidDType(fl);
|
||||||
|
addTypesp(newp);
|
||||||
|
m_voidp = newp;
|
||||||
|
}
|
||||||
|
return m_voidp;
|
||||||
|
}
|
||||||
|
|
||||||
AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd) {
|
AstBasicDType* AstTypeTable::findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd) {
|
||||||
if (m_basicps[kwd]) return m_basicps[kwd];
|
if (m_basicps[kwd]) return m_basicps[kwd];
|
||||||
//
|
//
|
||||||
|
|
@ -1075,10 +1105,18 @@ void AstTypeTable::dump(std::ostream& str) const {
|
||||||
}
|
}
|
||||||
// Note get newline from caller too.
|
// Note get newline from caller too.
|
||||||
}
|
}
|
||||||
|
void AstAssocArrayDType::dumpSmall(std::ostream& str) const {
|
||||||
|
this->AstNodeDType::dumpSmall(str);
|
||||||
|
str<<"[assoc-"<<(void*)keyDTypep()<<"]";
|
||||||
|
}
|
||||||
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
|
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
|
||||||
this->AstNodeDType::dumpSmall(str);
|
this->AstNodeDType::dumpSmall(str);
|
||||||
str<<"[]";
|
str<<"[]";
|
||||||
}
|
}
|
||||||
|
void AstVoidDType::dumpSmall(std::ostream& str) const {
|
||||||
|
this->AstNodeDType::dumpSmall(str);
|
||||||
|
str<<"void";
|
||||||
|
}
|
||||||
void AstVarScope::dump(std::ostream& str) const {
|
void AstVarScope::dump(std::ostream& str) const {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
if (isCircular()) str<<" [CIRC]";
|
if (isCircular()) str<<" [CIRC]";
|
||||||
|
|
|
||||||
179
src/V3AstNodes.h
179
src/V3AstNodes.h
|
|
@ -184,6 +184,22 @@ public:
|
||||||
virtual bool same(const AstNode* samep) const { return true; }
|
virtual bool same(const AstNode* samep) const { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AstAssocRange : public AstNodeRange {
|
||||||
|
// Associative array range specification
|
||||||
|
// Only for early parsing - becomes AstAssocDType
|
||||||
|
public:
|
||||||
|
explicit AstAssocRange(FileLine* fl, AstNodeDType* dtp)
|
||||||
|
: AstNodeRange(fl) {
|
||||||
|
setOp1p(dtp);
|
||||||
|
}
|
||||||
|
ASTNODE_NODE_FUNCS(AssocRange)
|
||||||
|
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||||
|
virtual string emitVerilog() { V3ERROR_NA; return ""; }
|
||||||
|
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||||
|
virtual bool same(const AstNode* samep) const { return true; }
|
||||||
|
AstNodeDType* keyDTypep() const { return VN_CAST(op1p(), NodeDType); }
|
||||||
|
};
|
||||||
|
|
||||||
class AstUnsizedRange : public AstNodeRange {
|
class AstUnsizedRange : public AstNodeRange {
|
||||||
// Unsized range specification, for open arrays
|
// Unsized range specification, for open arrays
|
||||||
public:
|
public:
|
||||||
|
|
@ -333,6 +349,65 @@ public:
|
||||||
void name(const string& flag) { m_name = flag; }
|
void name(const string& flag) { m_name = flag; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AstAssocArrayDType : public AstNodeDType {
|
||||||
|
// Associative array data type, ie "[some_dtype]"
|
||||||
|
// Children: DTYPE (moved to refDTypep() in V3Width)
|
||||||
|
// Children: DTYPE (the key, which remains here as a pointer)
|
||||||
|
private:
|
||||||
|
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
|
||||||
|
AstNodeDType* m_keyDTypep; // Keys of this type (after widthing)
|
||||||
|
public:
|
||||||
|
AstAssocArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstNodeDType* keyDtp)
|
||||||
|
: AstNodeDType(fl) {
|
||||||
|
childDTypep(dtp); // Only for parser
|
||||||
|
keyChildDTypep(keyDtp); // Only for parser
|
||||||
|
refDTypep(NULL);
|
||||||
|
keyDTypep(NULL);
|
||||||
|
dtypep(NULL); // V3Width will resolve
|
||||||
|
}
|
||||||
|
ASTNODE_NODE_FUNCS(AssocArrayDType)
|
||||||
|
virtual const char* broken() const {
|
||||||
|
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||||
|
|| (!m_refDTypep && childDTypep())));
|
||||||
|
BROKEN_RTN(!((m_keyDTypep && !childDTypep() && m_keyDTypep->brokeExists())
|
||||||
|
|| (!m_keyDTypep && childDTypep())));
|
||||||
|
return NULL; }
|
||||||
|
virtual void cloneRelink() {
|
||||||
|
if (m_refDTypep && m_refDTypep->clonep()) { m_refDTypep = m_refDTypep->clonep(); }
|
||||||
|
if (m_keyDTypep && m_keyDTypep->clonep()) { m_keyDTypep = m_keyDTypep->clonep(); } }
|
||||||
|
virtual bool same(const AstNode* samep) const {
|
||||||
|
const AstAssocArrayDType* asamep = static_cast<const AstAssocArrayDType*>(samep);
|
||||||
|
return (subDTypep() == asamep->subDTypep()
|
||||||
|
&& keyDTypep() == asamep->keyDTypep()); }
|
||||||
|
virtual bool similarDType(AstNodeDType* samep) const {
|
||||||
|
const AstAssocArrayDType* asamep = static_cast<const AstAssocArrayDType*>(samep);
|
||||||
|
return (subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp()));
|
||||||
|
}
|
||||||
|
virtual void dumpSmall(std::ostream& str) const;
|
||||||
|
virtual V3Hash sameHash() const { return V3Hash(m_refDTypep); }
|
||||||
|
AstNodeDType* getChildDTypep() const { return childDTypep(); }
|
||||||
|
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable
|
||||||
|
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
|
||||||
|
virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||||
|
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||||
|
virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; }
|
||||||
|
virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); }
|
||||||
|
virtual AstNodeDType* virtRefDType2p() const { return m_keyDTypep; }
|
||||||
|
virtual void virtRefDType2p(AstNodeDType* nodep) { keyDTypep(nodep); }
|
||||||
|
//
|
||||||
|
AstNodeDType* keyDTypep() const { return m_keyDTypep ? m_keyDTypep : keyChildDTypep(); }
|
||||||
|
void keyDTypep(AstNodeDType* nodep) { m_keyDTypep = nodep; }
|
||||||
|
AstNodeDType* keyChildDTypep() const { return VN_CAST(op2p(), NodeDType); } // op1 = Range of variable
|
||||||
|
void keyChildDTypep(AstNodeDType* nodep) { setOp2p(nodep); }
|
||||||
|
// METHODS
|
||||||
|
virtual AstBasicDType* basicp() const { return NULL; } // (Slow) recurse down to find basic data type
|
||||||
|
virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; }
|
||||||
|
virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; }
|
||||||
|
virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; }
|
||||||
|
virtual int widthAlignBytes() const { return subDTypep()->widthAlignBytes(); }
|
||||||
|
virtual int widthTotalBytes() const { return subDTypep()->widthTotalBytes(); }
|
||||||
|
};
|
||||||
|
|
||||||
class AstPackArrayDType : public AstNodeArrayDType {
|
class AstPackArrayDType : public AstNodeArrayDType {
|
||||||
// Packed array data type, ie "some_dtype [2:0] var_name"
|
// Packed array data type, ie "some_dtype [2:0] var_name"
|
||||||
// Children: DTYPE (moved to refDTypep() in V3Width)
|
// Children: DTYPE (moved to refDTypep() in V3Width)
|
||||||
|
|
@ -774,6 +849,28 @@ public:
|
||||||
void lsb(int lsb) { m_lsb = lsb; }
|
void lsb(int lsb) { m_lsb = lsb; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AstVoidDType : public AstNodeDType {
|
||||||
|
// For e.g. a function returning void
|
||||||
|
public:
|
||||||
|
AstVoidDType(FileLine* fl)
|
||||||
|
: AstNodeDType(fl) { dtypep(this); }
|
||||||
|
ASTNODE_NODE_FUNCS(VoidDType)
|
||||||
|
virtual void dumpSmall(std::ostream& str) const;
|
||||||
|
virtual bool hasDType() const { return true; }
|
||||||
|
virtual bool maybePointedTo() const { return true; }
|
||||||
|
virtual AstNodeDType* subDTypep() const { return NULL; }
|
||||||
|
virtual AstNodeDType* virtRefDTypep() const { return NULL; }
|
||||||
|
virtual void virtRefDTypep(AstNodeDType* nodep) { }
|
||||||
|
virtual bool similarDType(AstNodeDType* samep) const { return this==samep; }
|
||||||
|
virtual AstBasicDType* basicp() const { return NULL; }
|
||||||
|
virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; }
|
||||||
|
virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; }
|
||||||
|
virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; }
|
||||||
|
virtual int widthAlignBytes() const { return 1; }
|
||||||
|
virtual int widthTotalBytes() const { return 1; }
|
||||||
|
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||||
|
};
|
||||||
|
|
||||||
class AstEnumItem : public AstNode {
|
class AstEnumItem : public AstNode {
|
||||||
private:
|
private:
|
||||||
string m_name;
|
string m_name;
|
||||||
|
|
@ -927,6 +1024,40 @@ public:
|
||||||
static AstNode* baseFromp(AstNode* nodep); ///< What is the base variable (or const) this dereferences?
|
static AstNode* baseFromp(AstNode* nodep); ///< What is the base variable (or const) this dereferences?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AstAssocSel : public AstNodeSel {
|
||||||
|
// Parents: math|stmt
|
||||||
|
// Children: varref|arraysel, math
|
||||||
|
private:
|
||||||
|
void init(AstNode* fromp) {
|
||||||
|
if (fromp && VN_IS(fromp->dtypep()->skipRefp(), AssocArrayDType)) {
|
||||||
|
// Strip off array to find what array references
|
||||||
|
dtypeFrom(VN_CAST(fromp->dtypep()->skipRefp(), AssocArrayDType)->subDTypep());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
AstAssocSel(FileLine* fl, AstNode* fromp, AstNode* bitp)
|
||||||
|
: AstNodeSel(fl, fromp, bitp) {
|
||||||
|
init(fromp);
|
||||||
|
}
|
||||||
|
ASTNODE_NODE_FUNCS(AssocSel)
|
||||||
|
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) {
|
||||||
|
return new AstAssocSel(this->fileline(), lhsp, rhsp); }
|
||||||
|
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) {
|
||||||
|
V3ERROR_NA; }
|
||||||
|
virtual string emitVerilog() { return "%k(%l%f[%r])"; }
|
||||||
|
virtual string emitC() { return "%li%k[%ri]"; }
|
||||||
|
virtual bool cleanOut() const { return true; }
|
||||||
|
virtual bool cleanLhs() const { return false; }
|
||||||
|
virtual bool cleanRhs() const { return true; }
|
||||||
|
virtual bool sizeMattersLhs() const { return false; }
|
||||||
|
virtual bool sizeMattersRhs() const { return false; }
|
||||||
|
virtual bool isGateOptimizable() const { return true; } // esp for V3Const::ifSameAssign
|
||||||
|
virtual bool isPredictOptimizable() const { return false; }
|
||||||
|
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||||
|
virtual bool same(const AstNode* samep) const { return true; }
|
||||||
|
virtual int instrCount() const { return widthInstrs(); }
|
||||||
|
};
|
||||||
|
|
||||||
class AstWordSel : public AstNodeSel {
|
class AstWordSel : public AstNodeSel {
|
||||||
// Select a single word from a multi-word wide value
|
// Select a single word from a multi-word wide value
|
||||||
public:
|
public:
|
||||||
|
|
@ -1119,7 +1250,7 @@ class AstMethodCall : public AstNode {
|
||||||
// We do not support generic member calls yet, so this is only enough to
|
// We do not support generic member calls yet, so this is only enough to
|
||||||
// make built-in methods work
|
// make built-in methods work
|
||||||
private:
|
private:
|
||||||
string m_name; // Name of variable
|
string m_name; // Name of method
|
||||||
public:
|
public:
|
||||||
AstMethodCall(FileLine* fl, AstNode* fromp, VFlagChildDType, const string& name, AstNode* pinsp)
|
AstMethodCall(FileLine* fl, AstNode* fromp, VFlagChildDType, const string& name, AstNode* pinsp)
|
||||||
: AstNode(fl), m_name(name) {
|
: AstNode(fl), m_name(name) {
|
||||||
|
|
@ -1141,6 +1272,45 @@ public:
|
||||||
void addPinsp(AstNode* nodep) { addOp2p(nodep); }
|
void addPinsp(AstNode* nodep) { addOp2p(nodep); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AstCMethodCall : public AstNodeStmt {
|
||||||
|
// A reference to a "C" hardocded member task (or function)
|
||||||
|
// PARENTS: stmt/math
|
||||||
|
// Not all calls are statments vs math. AstNodeStmt needs isStatement() to deal.
|
||||||
|
private:
|
||||||
|
string m_name; // Name of method
|
||||||
|
bool m_pure; // Pure optimizable
|
||||||
|
bool m_statement; // Is a statement (AstNodeMath-like) versus AstNodeStmt-like
|
||||||
|
public:
|
||||||
|
AstCMethodCall(FileLine* fl, AstNode* fromp, VFlagChildDType, const string& name,
|
||||||
|
AstNode* pinsp)
|
||||||
|
: AstNodeStmt(fl), m_name(name), m_pure(false), m_statement(false) {
|
||||||
|
setOp1p(fromp);
|
||||||
|
dtypep(NULL); // V3Width will resolve
|
||||||
|
addNOp2p(pinsp);
|
||||||
|
}
|
||||||
|
AstCMethodCall(FileLine* fl, AstNode* fromp, const string& name, AstNode* pinsp)
|
||||||
|
: AstNodeStmt(fl), m_name(name), m_statement(false) {
|
||||||
|
setOp1p(fromp);
|
||||||
|
addNOp2p(pinsp);
|
||||||
|
}
|
||||||
|
ASTNODE_NODE_FUNCS(CMethodCall)
|
||||||
|
virtual string name() const { return m_name; } // * = Var name
|
||||||
|
virtual bool hasDType() const { return true; }
|
||||||
|
virtual void name(const string& name) { m_name = name; }
|
||||||
|
virtual V3Hash sameHash() const { return V3Hash(m_name); }
|
||||||
|
virtual bool same(const AstNode* samep) const {
|
||||||
|
const AstCMethodCall* asamep = static_cast<const AstCMethodCall*>(samep);
|
||||||
|
return (m_name == asamep->m_name); }
|
||||||
|
virtual bool isStatement() const { return m_statement; }
|
||||||
|
virtual bool isPure() const { return m_pure; }
|
||||||
|
void pure(bool flag) { m_pure = flag; }
|
||||||
|
void makeStatement() { m_statement = true; dtypeSetVoid(); }
|
||||||
|
AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing)
|
||||||
|
void fromp(AstNode* nodep) { setOp1p(nodep); }
|
||||||
|
AstNode* pinsp() const { return op2p(); } // op2 = Pin interconnection list
|
||||||
|
void addPinsp(AstNode* nodep) { addOp2p(nodep); }
|
||||||
|
};
|
||||||
|
|
||||||
class AstVar : public AstNode {
|
class AstVar : public AstNode {
|
||||||
// A variable (in/out/wire/reg/param) inside a module
|
// A variable (in/out/wire/reg/param) inside a module
|
||||||
private:
|
private:
|
||||||
|
|
@ -1412,7 +1582,8 @@ public:
|
||||||
string mtasksString() const;
|
string mtasksString() const;
|
||||||
private:
|
private:
|
||||||
class VlArgTypeRecursed;
|
class VlArgTypeRecursed;
|
||||||
VlArgTypeRecursed vlArgTypeRecurse(bool forFunc, const AstNodeDType* dtypep) const;
|
VlArgTypeRecursed vlArgTypeRecurse(bool forFunc, const AstNodeDType* dtypep,
|
||||||
|
bool arrayed) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AstDefParam : public AstNode {
|
class AstDefParam : public AstNode {
|
||||||
|
|
@ -6333,17 +6504,19 @@ public:
|
||||||
class AstTypeTable : public AstNode {
|
class AstTypeTable : public AstNode {
|
||||||
// Container for hash of standard data types
|
// Container for hash of standard data types
|
||||||
// Children: NODEDTYPEs
|
// Children: NODEDTYPEs
|
||||||
|
AstVoidDType* m_voidp;
|
||||||
AstBasicDType* m_basicps[AstBasicDTypeKwd::_ENUM_MAX];
|
AstBasicDType* m_basicps[AstBasicDTypeKwd::_ENUM_MAX];
|
||||||
//
|
//
|
||||||
typedef std::map<VBasicTypeKey,AstBasicDType*> DetailedMap;
|
typedef std::map<VBasicTypeKey,AstBasicDType*> DetailedMap;
|
||||||
DetailedMap m_detailedMap;
|
DetailedMap m_detailedMap;
|
||||||
public:
|
public:
|
||||||
explicit AstTypeTable(FileLine* fl) : AstNode(fl) {
|
explicit AstTypeTable(FileLine* fl) : AstNode(fl), m_voidp(NULL) {
|
||||||
for (int i=0; i<AstBasicDTypeKwd::_ENUM_MAX; ++i) m_basicps[i] = NULL;
|
for (int i=0; i<AstBasicDTypeKwd::_ENUM_MAX; ++i) m_basicps[i] = NULL;
|
||||||
}
|
}
|
||||||
ASTNODE_NODE_FUNCS(TypeTable)
|
ASTNODE_NODE_FUNCS(TypeTable)
|
||||||
AstNodeDType* typesp() const { return VN_CAST(op1p(), NodeDType);} // op1 = List of dtypes
|
AstNodeDType* typesp() const { return VN_CAST(op1p(), NodeDType);} // op1 = List of dtypes
|
||||||
void addTypesp(AstNodeDType* nodep) { addOp1p(nodep); }
|
void addTypesp(AstNodeDType* nodep) { addOp1p(nodep); }
|
||||||
|
AstVoidDType* findVoidDType(FileLine* fl);
|
||||||
AstBasicDType* findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd);
|
AstBasicDType* findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd);
|
||||||
AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd,
|
AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd,
|
||||||
int width, int widthMin, AstNumeric numeric);
|
int width, int widthMin, AstNumeric numeric);
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,9 @@ private:
|
||||||
void computeCppWidth(AstNode* nodep) {
|
void computeCppWidth(AstNode* nodep) {
|
||||||
if (!nodep->user2() && nodep->hasDType()) {
|
if (!nodep->user2() && nodep->hasDType()) {
|
||||||
if (VN_IS(nodep, Var) || VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|
if (VN_IS(nodep, Var) || VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|
||||||
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { // Or arrays
|
|| VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays
|
||||||
|
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|
||||||
|
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
|
||||||
} else {
|
} else {
|
||||||
setCppWidth(nodep);
|
setCppWidth(nodep);
|
||||||
}
|
}
|
||||||
|
|
@ -276,6 +278,11 @@ private:
|
||||||
insureCleanAndNext(nodep->argsp());
|
insureCleanAndNext(nodep->argsp());
|
||||||
setClean(nodep, true);
|
setClean(nodep, true);
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstCMethodCall* nodep) {
|
||||||
|
iterateChildren(nodep);
|
||||||
|
insureCleanAndNext(nodep->pinsp());
|
||||||
|
setClean(nodep, true);
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------
|
//--------------------
|
||||||
// Default: Just iterate
|
// Default: Just iterate
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,9 @@ private:
|
||||||
if (AstNode* subnodep = nodep->virtRefDTypep()) {
|
if (AstNode* subnodep = nodep->virtRefDTypep()) {
|
||||||
subnodep->user1Inc();
|
subnodep->user1Inc();
|
||||||
}
|
}
|
||||||
|
if (AstNode* subnodep = nodep->virtRefDType2p()) {
|
||||||
|
subnodep->user1Inc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,9 @@ public:
|
||||||
} else if (nodep->isWide()
|
} else if (nodep->isWide()
|
||||||
&& VN_IS(nodep->lhsp(), VarRef)
|
&& VN_IS(nodep->lhsp(), VarRef)
|
||||||
&& !VN_IS(nodep->rhsp(), CMath)
|
&& !VN_IS(nodep->rhsp(), CMath)
|
||||||
|
&& !VN_IS(nodep->rhsp(), CMethodCall)
|
||||||
&& !VN_IS(nodep->rhsp(), VarRef)
|
&& !VN_IS(nodep->rhsp(), VarRef)
|
||||||
|
&& !VN_IS(nodep->rhsp(), AssocSel)
|
||||||
&& !VN_IS(nodep->rhsp(), ArraySel)) {
|
&& !VN_IS(nodep->rhsp(), ArraySel)) {
|
||||||
// Wide functions assign into the array directly, don't need separate assign statement
|
// Wide functions assign into the array directly, don't need separate assign statement
|
||||||
m_wideTempRefp = VN_CAST(nodep->lhsp(), VarRef);
|
m_wideTempRefp = VN_CAST(nodep->lhsp(), VarRef);
|
||||||
|
|
@ -215,6 +217,27 @@ public:
|
||||||
}
|
}
|
||||||
virtual void visit(AstAlwaysPublic*) {
|
virtual void visit(AstAlwaysPublic*) {
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstAssocSel* nodep) {
|
||||||
|
iterateAndNextNull(nodep->fromp());
|
||||||
|
putbs(".at(");
|
||||||
|
AstAssocArrayDType* adtypep = VN_CAST(nodep->fromp()->dtypep(), AssocArrayDType);
|
||||||
|
UASSERT_OBJ(adtypep, nodep, "Associative select on non-associative type");
|
||||||
|
if (adtypep->keyDTypep()->isWide()) {
|
||||||
|
// Container class must take non-C-array (pointer) argument, so convert
|
||||||
|
putbs("VL_CVT_W_A(");
|
||||||
|
iterateAndNextNull(nodep->bitp());
|
||||||
|
puts(", ");
|
||||||
|
iterateAndNextNull(nodep->fromp());
|
||||||
|
putbs(".atDefault()"); // Not accessed; only to get the proper type of values
|
||||||
|
puts(")");
|
||||||
|
} else {
|
||||||
|
iterateAndNextNull(nodep->bitp());
|
||||||
|
}
|
||||||
|
puts(")");
|
||||||
|
if (nodep->dtypep()->isWide()) {
|
||||||
|
puts(".data()"); // Access returned std::array as C array
|
||||||
|
}
|
||||||
|
}
|
||||||
virtual void visit(AstCCall* nodep) {
|
virtual void visit(AstCCall* nodep) {
|
||||||
puts(nodep->hiernameProtect());
|
puts(nodep->hiernameProtect());
|
||||||
puts(nodep->funcp()->nameProtect());
|
puts(nodep->funcp()->nameProtect());
|
||||||
|
|
@ -233,6 +256,23 @@ public:
|
||||||
puts(");\n");
|
puts(");\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstCMethodCall* nodep) {
|
||||||
|
iterate(nodep->fromp());
|
||||||
|
puts(".");
|
||||||
|
puts(nodep->nameProtect());
|
||||||
|
puts("(");
|
||||||
|
bool comma = false;
|
||||||
|
for (AstNode* subnodep = nodep->pinsp(); subnodep; subnodep = subnodep->nextp()) {
|
||||||
|
if (comma) puts(", ");
|
||||||
|
iterate(subnodep);
|
||||||
|
comma = true;
|
||||||
|
}
|
||||||
|
puts(")");
|
||||||
|
// Some are statements some are math.
|
||||||
|
if (nodep->isStatement()) puts(";\n");
|
||||||
|
UASSERT_OBJ(!nodep->isStatement() || VN_IS(nodep->dtypep(), VoidDType),
|
||||||
|
nodep, "Statement of non-void data type");
|
||||||
|
}
|
||||||
virtual void visit(AstNodeCase* nodep) {
|
virtual void visit(AstNodeCase* nodep) {
|
||||||
// In V3Case...
|
// In V3Case...
|
||||||
nodep->v3fatalSrc("Case statements should have been reduced out");
|
nodep->v3fatalSrc("Case statements should have been reduced out");
|
||||||
|
|
@ -1303,7 +1343,12 @@ class EmitCImp : EmitCStmts {
|
||||||
dtypep = dtypep->skipRefp();
|
dtypep = dtypep->skipRefp();
|
||||||
AstBasicDType* basicp = dtypep->basicp();
|
AstBasicDType* basicp = dtypep->basicp();
|
||||||
// Returns string to do resetting, empty to do nothing (which caller should handle)
|
// Returns string to do resetting, empty to do nothing (which caller should handle)
|
||||||
if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
if (AstAssocArrayDType* adtypep = VN_CAST(dtypep, AssocArrayDType)) {
|
||||||
|
string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : ""); // Access std::array as C array
|
||||||
|
return emitVarResetRecurse(varp, adtypep->subDTypep(), depth+1,
|
||||||
|
".atDefault()" + cvtarray);
|
||||||
|
}
|
||||||
|
else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||||
UASSERT_OBJ(adtypep->msb() >= adtypep->lsb(), varp,
|
UASSERT_OBJ(adtypep->msb() >= adtypep->lsb(), varp,
|
||||||
"Should have swapped msb & lsb earlier.");
|
"Should have swapped msb & lsb earlier.");
|
||||||
string ivar = string("__Vi")+cvtToStr(depth);
|
string ivar = string("__Vi")+cvtToStr(depth);
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,10 @@ class EmitCInlines : EmitCBaseVisitor {
|
||||||
v3Global.needHeavy(true);
|
v3Global.needHeavy(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstAssocArrayDType* nodep) {
|
||||||
|
v3Global.needHeavy(true);
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
virtual void visit(AstValuePlusArgs* nodep) {
|
virtual void visit(AstValuePlusArgs* nodep) {
|
||||||
v3Global.needHeavy(true);
|
v3Global.needHeavy(true);
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,11 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep,
|
||||||
} else if (VN_IS(nrangep, UnsizedRange)) {
|
} else if (VN_IS(nrangep, UnsizedRange)) {
|
||||||
arrayp = new AstUnsizedArrayDType
|
arrayp = new AstUnsizedArrayDType
|
||||||
(nrangep->fileline(), VFlagChildDType(), arrayp);
|
(nrangep->fileline(), VFlagChildDType(), arrayp);
|
||||||
|
} else if (VN_IS(nrangep, AssocRange)) {
|
||||||
|
AstAssocRange* arangep = VN_CAST(nrangep, AssocRange);
|
||||||
|
AstNodeDType* keyp = arangep->keyDTypep(); keyp->unlinkFrBack();
|
||||||
|
arrayp = new AstAssocArrayDType
|
||||||
|
(nrangep->fileline(), VFlagChildDType(), arrayp, keyp);
|
||||||
} else {
|
} else {
|
||||||
UASSERT_OBJ(0, nrangep, "Expected range or unsized range");
|
UASSERT_OBJ(0, nrangep, "Expected range or unsized range");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -349,6 +349,16 @@ private:
|
||||||
}
|
}
|
||||||
checkNode(nodep);
|
checkNode(nodep);
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstAssocSel* nodep) {
|
||||||
|
iterateAndNextNull(nodep->fromp());
|
||||||
|
{ // Only the 'from' is part of the assignment LHS
|
||||||
|
bool prevAssign = m_assignLhs;
|
||||||
|
m_assignLhs = false;
|
||||||
|
iterateAndNextNull(nodep->bitp());
|
||||||
|
m_assignLhs = prevAssign;
|
||||||
|
}
|
||||||
|
checkNode(nodep);
|
||||||
|
}
|
||||||
virtual void visit(AstConst* nodep) {
|
virtual void visit(AstConst* nodep) {
|
||||||
iterateChildren(nodep); checkNode(nodep);
|
iterateChildren(nodep); checkNode(nodep);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,7 @@ private:
|
||||||
// Of a constant index
|
// Of a constant index
|
||||||
AstConst* lbitp = VN_CAST(lselp->bitp(), Const);
|
AstConst* lbitp = VN_CAST(lselp->bitp(), Const);
|
||||||
if (!lbitp) { mergeEnd(); return; }
|
if (!lbitp) { mergeEnd(); return; }
|
||||||
|
if (lbitp->width() > 32) { mergeEnd(); return; } // Assoc arrays can do this
|
||||||
uint32_t index = lbitp->toUInt();
|
uint32_t index = lbitp->toUInt();
|
||||||
// Of variable
|
// Of variable
|
||||||
AstNodeVarRef* lvarrefp = VN_CAST(lselp->fromp(), NodeVarRef);
|
AstNodeVarRef* lvarrefp = VN_CAST(lselp->fromp(), NodeVarRef);
|
||||||
|
|
|
||||||
124
src/V3Width.cpp
124
src/V3Width.cpp
|
|
@ -737,6 +737,20 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void visit(AstAssocSel* nodep) {
|
||||||
|
// Signed/Real: Output type based on array-declared type; binary operator
|
||||||
|
if (m_vup->prelim()) {
|
||||||
|
AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefp();
|
||||||
|
AstAssocArrayDType* adtypep = VN_CAST(fromDtp, AssocArrayDType);
|
||||||
|
if (!adtypep) {
|
||||||
|
UINFO(1," Related dtype: "<<fromDtp<<endl);
|
||||||
|
nodep->v3fatalSrc("Associative array reference is not to associative array");
|
||||||
|
}
|
||||||
|
iterateCheckTyped(nodep, "Associative select", nodep->bitp(), adtypep->keyDTypep(), BOTH);
|
||||||
|
nodep->dtypeFrom(adtypep->subDTypep());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual void visit(AstSliceSel* nodep) {
|
virtual void visit(AstSliceSel* nodep) {
|
||||||
// Always creates as output an unpacked array
|
// Always creates as output an unpacked array
|
||||||
if (m_vup->prelim()) {
|
if (m_vup->prelim()) {
|
||||||
|
|
@ -1052,6 +1066,16 @@ private:
|
||||||
}
|
}
|
||||||
UINFO(4,"dtWidthed "<<nodep<<endl);
|
UINFO(4,"dtWidthed "<<nodep<<endl);
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstAssocArrayDType* nodep) {
|
||||||
|
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
|
||||||
|
if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep));
|
||||||
|
if (nodep->keyChildDTypep()) nodep->keyDTypep(moveDTypeEdit(nodep, nodep->keyChildDTypep()));
|
||||||
|
// Iterate into subDTypep() to resolve that type and update pointer.
|
||||||
|
nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep()));
|
||||||
|
nodep->keyDTypep(iterateEditDTypep(nodep, nodep->keyDTypep()));
|
||||||
|
nodep->dtypep(nodep); // The array itself, not subDtype
|
||||||
|
UINFO(4,"dtWidthed "<<nodep<<endl);
|
||||||
|
}
|
||||||
virtual void visit(AstUnsizedArrayDType* nodep) {
|
virtual void visit(AstUnsizedArrayDType* nodep) {
|
||||||
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
|
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
|
||||||
if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep));
|
if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep));
|
||||||
|
|
@ -1589,6 +1613,7 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (VN_IS(fromDtp, EnumDType)
|
else if (VN_IS(fromDtp, EnumDType)
|
||||||
|
|| VN_IS(fromDtp, AssocArrayDType)
|
||||||
|| VN_IS(fromDtp, BasicDType)) {
|
|| VN_IS(fromDtp, BasicDType)) {
|
||||||
// Method call on enum without following parenthesis, e.g. "ENUM.next"
|
// Method call on enum without following parenthesis, e.g. "ENUM.next"
|
||||||
// Convert this into a method call, and let that visitor figure out what to do next
|
// Convert this into a method call, and let that visitor figure out what to do next
|
||||||
|
|
@ -1629,6 +1654,11 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void visit(AstCMethodCall* nodep) {
|
||||||
|
// Never created before V3Width, so no need to redo it
|
||||||
|
UASSERT_OBJ(nodep->dtypep(), nodep, "CMETHODCALLs should have already been sized");
|
||||||
|
}
|
||||||
|
|
||||||
virtual void visit(AstMethodCall* nodep) {
|
virtual void visit(AstMethodCall* nodep) {
|
||||||
UINFO(5," METHODSEL "<<nodep<<endl);
|
UINFO(5," METHODSEL "<<nodep<<endl);
|
||||||
if (nodep->didWidth()) return;
|
if (nodep->didWidth()) return;
|
||||||
|
|
@ -1648,6 +1678,9 @@ private:
|
||||||
if (AstEnumDType* adtypep = VN_CAST(fromDtp, EnumDType)) {
|
if (AstEnumDType* adtypep = VN_CAST(fromDtp, EnumDType)) {
|
||||||
methodCallEnum(nodep, adtypep);
|
methodCallEnum(nodep, adtypep);
|
||||||
}
|
}
|
||||||
|
else if (AstAssocArrayDType* adtypep = VN_CAST(fromDtp, AssocArrayDType)) {
|
||||||
|
methodCallAssoc(nodep, adtypep);
|
||||||
|
}
|
||||||
else if (AstUnpackArrayDType* adtypep = VN_CAST(fromDtp, UnpackArrayDType)) {
|
else if (AstUnpackArrayDType* adtypep = VN_CAST(fromDtp, UnpackArrayDType)) {
|
||||||
methodCallUnpack(nodep, adtypep);
|
methodCallUnpack(nodep, adtypep);
|
||||||
}
|
}
|
||||||
|
|
@ -1765,6 +1798,83 @@ private:
|
||||||
nodep->v3error("Unknown built-in enum method " << nodep->prettyNameQ());
|
nodep->v3error("Unknown built-in enum method " << nodep->prettyNameQ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void methodCallAssoc(AstMethodCall* nodep, AstAssocArrayDType* adtypep) {
|
||||||
|
AstCMethodCall* newp = NULL;
|
||||||
|
if (nodep->name() == "num" // function int num()
|
||||||
|
|| nodep->name() == "size") {
|
||||||
|
methodOkArguments(nodep, 0, 0);
|
||||||
|
newp = new AstCMethodCall(nodep->fileline(),
|
||||||
|
nodep->fromp()->unlinkFrBack(),
|
||||||
|
"size", NULL); // So don't need num()
|
||||||
|
newp->dtypeSetSigned32();
|
||||||
|
newp->didWidth(true);
|
||||||
|
newp->protect(false);
|
||||||
|
} else if (nodep->name() == "first" // function int first(ref index)
|
||||||
|
|| nodep->name() == "last"
|
||||||
|
|| nodep->name() == "next"
|
||||||
|
|| nodep->name() == "prev") {
|
||||||
|
methodOkArguments(nodep, 1, 1);
|
||||||
|
AstNode* index_exprp = methodCallAssocIndexExpr(nodep, adtypep);
|
||||||
|
newp = new AstCMethodCall(nodep->fileline(),
|
||||||
|
nodep->fromp()->unlinkFrBack(),
|
||||||
|
nodep->name(), // first/last/next/prev
|
||||||
|
index_exprp->unlinkFrBack());
|
||||||
|
newp->dtypeSetSigned32();
|
||||||
|
newp->protect(false);
|
||||||
|
newp->didWidth(true);
|
||||||
|
} else if (nodep->name() == "exists") { // function int exists(input index)
|
||||||
|
// IEEE really should have made this a "bit" return
|
||||||
|
methodOkArguments(nodep, 1, 1);
|
||||||
|
AstNode* index_exprp = methodCallAssocIndexExpr(nodep, adtypep);
|
||||||
|
newp = new AstCMethodCall(nodep->fileline(),
|
||||||
|
nodep->fromp()->unlinkFrBack(),
|
||||||
|
"exists", index_exprp->unlinkFrBack());
|
||||||
|
newp->dtypeSetSigned32();
|
||||||
|
newp->pure(true);
|
||||||
|
newp->protect(false);
|
||||||
|
newp->didWidth(true);
|
||||||
|
} else if (nodep->name() == "delete") { // function void delete([input integer index])
|
||||||
|
methodOkArguments(nodep, 0, 1);
|
||||||
|
methodCallLValue(nodep, nodep->fromp(), true);
|
||||||
|
if (!nodep->pinsp()) {
|
||||||
|
newp = new AstCMethodCall(nodep->fileline(),
|
||||||
|
nodep->fromp()->unlinkFrBack(),
|
||||||
|
"clear", NULL);
|
||||||
|
newp->protect(false);
|
||||||
|
newp->makeStatement();
|
||||||
|
} else {
|
||||||
|
AstNode* index_exprp = methodCallAssocIndexExpr(nodep, adtypep);
|
||||||
|
newp = new AstCMethodCall(nodep->fileline(),
|
||||||
|
nodep->fromp()->unlinkFrBack(),
|
||||||
|
"erase",
|
||||||
|
index_exprp->unlinkFrBack());
|
||||||
|
newp->protect(false);
|
||||||
|
newp->makeStatement();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nodep->v3error("Unknown built-in associative array method "<<nodep->prettyNameQ());
|
||||||
|
}
|
||||||
|
if (newp) {
|
||||||
|
newp->didWidth(true);
|
||||||
|
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AstNode* methodCallAssocIndexExpr(AstMethodCall* nodep, AstAssocArrayDType* adtypep) {
|
||||||
|
AstNode* index_exprp = VN_CAST(nodep->pinsp(), Arg)->exprp();
|
||||||
|
iterateCheck(nodep, "index", index_exprp, CONTEXT, FINAL,
|
||||||
|
adtypep->keyDTypep(), EXTEND_EXP);
|
||||||
|
VL_DANGLING(index_exprp); // May have been edited
|
||||||
|
return VN_CAST(nodep->pinsp(), Arg)->exprp();
|
||||||
|
}
|
||||||
|
void methodCallLValue(AstMethodCall* nodep, AstNode* childp, bool lvalue) {
|
||||||
|
AstNodeVarRef* varrefp = VN_CAST(childp, NodeVarRef);
|
||||||
|
if (!varrefp) {
|
||||||
|
nodep->v3error("Unsupported: Non-variable on LHS of built-in method '"
|
||||||
|
<<nodep->prettyName()<<"'");
|
||||||
|
} else {
|
||||||
|
if (lvalue) varrefp->lvalue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
void methodCallUnpack(AstMethodCall* nodep, AstUnpackArrayDType* adtypep) {
|
void methodCallUnpack(AstMethodCall* nodep, AstUnpackArrayDType* adtypep) {
|
||||||
enum { UNKNOWN = 0, ARRAY_OR, ARRAY_AND, ARRAY_XOR } methodId;
|
enum { UNKNOWN = 0, ARRAY_OR, ARRAY_AND, ARRAY_XOR } methodId;
|
||||||
|
|
||||||
|
|
@ -2314,13 +2424,25 @@ private:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'p': { // Packed
|
case 'p': { // Packed
|
||||||
AstBasicDType* basicp = argp ? argp->dtypep()->basicp() : NULL;
|
AstNodeDType* dtypep = argp ? argp->dtypep()->skipRefp() : NULL;
|
||||||
|
AstBasicDType* basicp = dtypep ? dtypep->basicp() : NULL;
|
||||||
if (basicp && basicp->isString()) {
|
if (basicp && basicp->isString()) {
|
||||||
added = true;
|
added = true;
|
||||||
newFormat += "\"%@\"";
|
newFormat += "\"%@\"";
|
||||||
} else if (basicp && basicp->isDouble()) {
|
} else if (basicp && basicp->isDouble()) {
|
||||||
added = true;
|
added = true;
|
||||||
newFormat += "%g";
|
newFormat += "%g";
|
||||||
|
} else if (VN_IS(dtypep, AssocArrayDType)) {
|
||||||
|
added = true;
|
||||||
|
newFormat += "%@";
|
||||||
|
AstNRelinker handle;
|
||||||
|
argp->unlinkFrBack(&handle);
|
||||||
|
AstCMethodCall* newp = new AstCMethodCall(
|
||||||
|
nodep->fileline(), argp, "to_string", NULL);
|
||||||
|
newp->dtypeSetString();
|
||||||
|
newp->pure(true);
|
||||||
|
newp->protect(false);
|
||||||
|
handle.relink(newp);
|
||||||
} else {
|
} else {
|
||||||
added = true;
|
added = true;
|
||||||
if (fmt == "%0") newFormat += "'h%0h"; // IEEE our choice
|
if (fmt == "%0") newFormat += "'h%0h"; // IEEE our choice
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,7 @@ private:
|
||||||
if (nodep->numeric().isNosign()) nodep->numeric(AstNumeric::UNSIGNED);
|
if (nodep->numeric().isNosign()) nodep->numeric(AstNumeric::UNSIGNED);
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
nodep->virtRefDTypep(editOneDType(nodep->virtRefDTypep()));
|
nodep->virtRefDTypep(editOneDType(nodep->virtRefDTypep()));
|
||||||
|
nodep->virtRefDType2p(editOneDType(nodep->virtRefDType2p()));
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodePreSel* nodep) { // LCOV_EXCL_LINE
|
virtual void visit(AstNodePreSel* nodep) { // LCOV_EXCL_LINE
|
||||||
// This check could go anywhere after V3Param
|
// This check could go anywhere after V3Param
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,8 @@ private:
|
||||||
if (const AstNodeArrayDType* adtypep = VN_CAST(ddtypep, NodeArrayDType)) {
|
if (const AstNodeArrayDType* adtypep = VN_CAST(ddtypep, NodeArrayDType)) {
|
||||||
fromRange = adtypep->declRange();
|
fromRange = adtypep->declRange();
|
||||||
}
|
}
|
||||||
|
else if (const AstAssocArrayDType* adtypep = VN_CAST(ddtypep, AssocArrayDType)) {
|
||||||
|
}
|
||||||
else if (const AstNodeClassDType* adtypep = VN_CAST(ddtypep, NodeClassDType)) {
|
else if (const AstNodeClassDType* adtypep = VN_CAST(ddtypep, NodeClassDType)) {
|
||||||
fromRange = adtypep->declRange();
|
fromRange = adtypep->declRange();
|
||||||
}
|
}
|
||||||
|
|
@ -246,6 +248,15 @@ private:
|
||||||
if (debug()>=9) newp->dumpTree(cout, "--SELBTn: ");
|
if (debug()>=9) newp->dumpTree(cout, "--SELBTn: ");
|
||||||
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
|
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||||
}
|
}
|
||||||
|
else if (AstAssocArrayDType* adtypep = VN_CAST(ddtypep, AssocArrayDType)) {
|
||||||
|
// SELBIT(array, index) -> ASSOCSEL(array, index)
|
||||||
|
AstNode* subp = rhsp;
|
||||||
|
AstAssocSel* newp = new AstAssocSel(nodep->fileline(),
|
||||||
|
fromp, subp);
|
||||||
|
newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference
|
||||||
|
if (debug()>=9) newp->dumpTree(cout, "--SELBTn: ");
|
||||||
|
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||||
|
}
|
||||||
else if (VN_IS(ddtypep, BasicDType)) {
|
else if (VN_IS(ddtypep, BasicDType)) {
|
||||||
// SELBIT(range, index) -> SEL(array, index, 1)
|
// SELBIT(range, index) -> SEL(array, index, 1)
|
||||||
AstSel* newp = new AstSel(nodep->fileline(),
|
AstSel* newp = new AstSel(nodep->fileline(),
|
||||||
|
|
|
||||||
|
|
@ -1627,9 +1627,9 @@ variable_dimension<rangep>: // ==IEEE: variable_dimension
|
||||||
| anyrange { $$ = $1; }
|
| anyrange { $$ = $1; }
|
||||||
| '[' constExpr ']' { $$ = new AstRange($1, new AstConst($1, 0), new AstSub($1, $2, new AstConst($1, 1))); }
|
| '[' constExpr ']' { $$ = new AstRange($1, new AstConst($1, 0), new AstSub($1, $2, new AstConst($1, 1))); }
|
||||||
// // IEEE: associative_dimension
|
// // IEEE: associative_dimension
|
||||||
//UNSUP '[' data_type ']' { UNSUP }
|
| '[' data_type ']' { $$ = new AstAssocRange($1, $2); }
|
||||||
//UNSUP yP_BRASTAR ']' { UNSUP }
|
| yP_BRASTAR ']' { $$ = NULL; v3error("Unsupported: [*] wildcard associative arrays"); }
|
||||||
//UNSUP '[' '*' ']' { UNSUP }
|
| '[' '*' ']' { $$ = NULL; v3error("Unsupported: [*] wildcard associative arrays"); }
|
||||||
// // IEEE: queue_dimension
|
// // IEEE: queue_dimension
|
||||||
// // '[' '$' ']' -- $ is part of expr
|
// // '[' '$' ']' -- $ is part of expr
|
||||||
// // '[' '$' ':' expr ']' -- anyrange:expr:$
|
// // '[' '$' ':' expr ']' -- anyrange:expr:$
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2019 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.
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
compile(
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed into the Public Domain, for any use,
|
||||||
|
// without warranty, 2019 by Wilson Snyder.
|
||||||
|
|
||||||
|
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||||
|
`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||||
|
`define checkg(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%g' exp='%g'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
integer cyc=0;
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
|
||||||
|
always @ (posedge clk) begin
|
||||||
|
cyc <= cyc + 1;
|
||||||
|
begin
|
||||||
|
// Type
|
||||||
|
typedef bit [3:0] nibble_t;
|
||||||
|
string a [nibble_t];
|
||||||
|
string b [nibble_t];
|
||||||
|
nibble_t k;
|
||||||
|
string v;
|
||||||
|
|
||||||
|
a[4'd3] = "fooed";
|
||||||
|
a[4'd2] = "bared";
|
||||||
|
i = a.num(); `checkh(i, 2);
|
||||||
|
i = a.size; `checkh(i, 2); // Also checks no parens
|
||||||
|
v = a[4'd3]; `checks(v, "fooed");
|
||||||
|
v = a[4'd2]; `checks(v, "bared");
|
||||||
|
i = a.exists(4'd0); `checkh(i, 0);
|
||||||
|
i = a.exists(4'd2); `checkh(i, 1);
|
||||||
|
i = a.first(k); `checkh(i, 1); `checks(k, 4'd2);
|
||||||
|
i = a.next(k); `checkh(i, 1); `checks(k, 4'd3);
|
||||||
|
i = a.next(k); `checkh(i, 0);
|
||||||
|
i = a.last(k); `checkh(i, 1); `checks(k, 4'd3);
|
||||||
|
i = a.prev(k); `checkh(i, 1); `checks(k, 4'd2);
|
||||||
|
i = a.prev(k); `checkh(i, 0);
|
||||||
|
v = $sformatf("%p", a); `checks(v, "'{'h2:\"bared\", 'h3:\"fooed\"} ");
|
||||||
|
|
||||||
|
a.delete(4'd2);
|
||||||
|
i = a.size(); `checkh(i, 1);
|
||||||
|
|
||||||
|
b = a; // Copy assignment
|
||||||
|
i = b.size(); `checkh(i, 1);
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
// Strings
|
||||||
|
string a [string];
|
||||||
|
string k;
|
||||||
|
string v;
|
||||||
|
|
||||||
|
a["foo"] = "fooed";
|
||||||
|
a["bar"] = "bared";
|
||||||
|
i = a.num(); `checkh(i, 2);
|
||||||
|
i = a.size(); `checkh(i, 2);
|
||||||
|
v = a["foo"]; `checks(v, "fooed");
|
||||||
|
v = a["bar"]; `checks(v, "bared");
|
||||||
|
i = a.exists("baz"); `checkh(i, 0);
|
||||||
|
i = a.exists("bar"); `checkh(i, 1);
|
||||||
|
i = a.first(k); `checkh(i, 1); `checks(k, "bar");
|
||||||
|
i = a.next(k); `checkh(i, 1); `checks(k, "foo");
|
||||||
|
i = a.next(k); `checkh(i, 0);
|
||||||
|
i = a.last(k); `checkh(i, 1); `checks(k, "foo");
|
||||||
|
i = a.prev(k); `checkh(i, 1); `checks(k, "bar");
|
||||||
|
i = a.prev(k); `checkh(i, 0);
|
||||||
|
v = $sformatf("%p", a["foo"]); `checks(v, "\"fooed\"");
|
||||||
|
v = $sformatf("%p", a); `checks(v, "'{\"bar\":\"bared\", \"foo\":\"fooed\"} ");
|
||||||
|
|
||||||
|
a.delete("bar");
|
||||||
|
i = a.size(); `checkh(i, 1);
|
||||||
|
a.delete();
|
||||||
|
i = a.size(); `checkh(i, 0);
|
||||||
|
i = a.first(k); `checkh(i, 0);
|
||||||
|
i = a.last(k); `checkh(i, 0);
|
||||||
|
|
||||||
|
// Patterns & default
|
||||||
|
`ifndef VERILATOR // Unsupported: Pattern assignment
|
||||||
|
a = '{ "f": "fooed", "b": "bared", default: "defaulted" };
|
||||||
|
i = a.size(); `checkh(i, 2); // Default doesn't count
|
||||||
|
v = a["f"]; `checks(v, "fooed");
|
||||||
|
v = a["b"]; `checks(v, "bared");
|
||||||
|
v = a["NEXISTS"]; `checks(v, "defaulted");
|
||||||
|
`endif
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
// Wide-wides - need special array container classes, ick.
|
||||||
|
logic [91:2] a [ logic [65:1] ];
|
||||||
|
a[~65'hfe] = ~ 90'hfee;
|
||||||
|
`checkh(a[~65'hfe], ~ 90'hfee);
|
||||||
|
end
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
%Error: t/t_assoc_meth_bad.v:13: The 1 arguments passed to .num method does not match its requiring 0 arguments
|
||||||
|
: ... In instance t
|
||||||
|
v = a.num("badarg");
|
||||||
|
^~~
|
||||||
|
%Error: t/t_assoc_meth_bad.v:14: The 1 arguments passed to .size method does not match its requiring 0 arguments
|
||||||
|
: ... In instance t
|
||||||
|
v = a.size("badarg");
|
||||||
|
^~~~
|
||||||
|
%Error: t/t_assoc_meth_bad.v:15: The 0 arguments passed to .exists method does not match its requiring 1 arguments
|
||||||
|
: ... In instance t
|
||||||
|
v = a.exists();
|
||||||
|
^~~~~~
|
||||||
|
%Error: t/t_assoc_meth_bad.v:16: The 2 arguments passed to .exists method does not match its requiring 1 arguments
|
||||||
|
: ... In instance t
|
||||||
|
v = a.exists(k, "bad2");
|
||||||
|
^~~~~~
|
||||||
|
%Error: t/t_assoc_meth_bad.v:17: The 0 arguments passed to .first method does not match its requiring 1 arguments
|
||||||
|
: ... In instance t
|
||||||
|
v = a.first();
|
||||||
|
^~~~~
|
||||||
|
%Error: t/t_assoc_meth_bad.v:18: The 2 arguments passed to .next method does not match its requiring 1 arguments
|
||||||
|
: ... In instance t
|
||||||
|
v = a.next(k, "bad2");
|
||||||
|
^~~~
|
||||||
|
%Error: t/t_assoc_meth_bad.v:19: The 0 arguments passed to .last method does not match its requiring 1 arguments
|
||||||
|
: ... In instance t
|
||||||
|
v = a.last();
|
||||||
|
^~~~
|
||||||
|
%Error: t/t_assoc_meth_bad.v:20: The 2 arguments passed to .prev method does not match its requiring 1 arguments
|
||||||
|
: ... In instance t
|
||||||
|
v = a.prev(k, "bad2");
|
||||||
|
^~~~
|
||||||
|
%Error: t/t_assoc_meth_bad.v:21: The 2 arguments passed to .delete method does not match its requiring 0 to 1 arguments
|
||||||
|
: ... In instance t
|
||||||
|
a.delete(k, "bad2");
|
||||||
|
^~~~~~
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2019 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.
|
||||||
|
|
||||||
|
scenarios(vlt => 1);
|
||||||
|
|
||||||
|
lint(
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed into the Public Domain, for any use,
|
||||||
|
// without warranty, 2019 by Wilson Snyder.
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/);
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
string a [string];
|
||||||
|
string k;
|
||||||
|
string v;
|
||||||
|
|
||||||
|
v = a.num("badarg");
|
||||||
|
v = a.size("badarg");
|
||||||
|
v = a.exists(); // Bad
|
||||||
|
v = a.exists(k, "bad2");
|
||||||
|
v = a.first(); // Bad
|
||||||
|
v = a.next(k, "bad2"); // Bad
|
||||||
|
v = a.last(); // Bad
|
||||||
|
v = a.prev(k, "bad2"); // Bad
|
||||||
|
a.delete(k, "bad2");
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
%Error: t/t_assoc_pattern_unsup.v:18: Unsupported: Assignment pattern applies against non struct/union: ASSOCARRAYDTYPE
|
||||||
|
: ... In instance t
|
||||||
|
a = '{ "f": "fooed", "b": "bared", default: "defaulted" };
|
||||||
|
^~
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2019 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.
|
||||||
|
|
||||||
|
scenarios(vlt => 1);
|
||||||
|
|
||||||
|
lint(
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed into the Public Domain, for any use,
|
||||||
|
// without warranty, 2019 by Wilson Snyder.
|
||||||
|
|
||||||
|
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||||
|
`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||||
|
`define checkg(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%g' exp='%g'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/);
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
int i;
|
||||||
|
string a [string];
|
||||||
|
string k;
|
||||||
|
string v;
|
||||||
|
|
||||||
|
a = '{ "f": "fooed", "b": "bared", default: "defaulted" };
|
||||||
|
i = a.size(); `checkh(i, 2); // Default doesn't count
|
||||||
|
v = a["f"]; `checks(v, "fooed");
|
||||||
|
v = a["b"]; `checks(v, "bared");
|
||||||
|
v = a["NEXISTS"]; `checks(v, "defaulted");
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
%Error: Unsupported: [*] wildcard associative arrays
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2019 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.
|
||||||
|
|
||||||
|
scenarios(vlt => 1);
|
||||||
|
|
||||||
|
lint(
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed into the Public Domain, for any use,
|
||||||
|
// without warranty, 2019 by Wilson Snyder.
|
||||||
|
|
||||||
|
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||||
|
`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||||
|
`define checkg(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%g' exp='%g'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
integer cyc=0;
|
||||||
|
|
||||||
|
integer i;
|
||||||
|
|
||||||
|
always @ (posedge clk) begin
|
||||||
|
cyc <= cyc + 1;
|
||||||
|
begin
|
||||||
|
// Wildcard
|
||||||
|
string a [*];
|
||||||
|
int k;
|
||||||
|
string v;
|
||||||
|
|
||||||
|
a[32'd1234] = "fooed";
|
||||||
|
a[4'd3] = "bared";
|
||||||
|
i = a.num(); `checkh(i, 2);
|
||||||
|
i = a.size(); `checkh(i, 2);
|
||||||
|
v = a[32'd1234]; `checks(v, "fooed");
|
||||||
|
v = a[4'd3]; `checks(v, "bared");
|
||||||
|
i = a.exists("baz"); `checkh(i, 0);
|
||||||
|
i = a.exists(4'd3); `checkh(i, 1);
|
||||||
|
i = a.first(k); `checkh(i, 1); `checks(k, 4'd3);
|
||||||
|
i = a.next(k); `checkh(i, 1); `checks(k, 32'd1234);
|
||||||
|
i = a.next(k); `checkh(i, 0);
|
||||||
|
i = a.last(k); `checkh(i, 1); `checks(k, 32'd1234);
|
||||||
|
i = a.prev(k); `checkh(i, 1); `checks(k, 4'd3);
|
||||||
|
i = a.prev(k); `checkh(i, 0);
|
||||||
|
a.delete(4'd3);
|
||||||
|
i = a.size(); `checkh(i, 1);
|
||||||
|
end
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -34,6 +34,7 @@ module sub (/*AUTOARG*/
|
||||||
real r;
|
real r;
|
||||||
string s,s2;
|
string s,s2;
|
||||||
string sarr[2:1];
|
string sarr[2:1];
|
||||||
|
string assoc[string];
|
||||||
|
|
||||||
string si;
|
string si;
|
||||||
|
|
||||||
|
|
@ -63,6 +64,7 @@ module sub (/*AUTOARG*/
|
||||||
s <= "hello";
|
s <= "hello";
|
||||||
sarr[1] <= "sarr[1]";
|
sarr[1] <= "sarr[1]";
|
||||||
sarr[2] <= "sarr[2]";
|
sarr[2] <= "sarr[2]";
|
||||||
|
assoc["mapped"] <= "Is mapped";
|
||||||
end
|
end
|
||||||
if (cyc==1) begin
|
if (cyc==1) begin
|
||||||
if ($test$plusargs("save_restore")!=0) begin
|
if ($test$plusargs("save_restore")!=0) begin
|
||||||
|
|
@ -88,8 +90,9 @@ module sub (/*AUTOARG*/
|
||||||
$display("%s",s);
|
$display("%s",s);
|
||||||
$display("%s",sarr[1]);
|
$display("%s",sarr[1]);
|
||||||
$display("%s",sarr[2]);
|
$display("%s",sarr[2]);
|
||||||
$write("*-* All Finished *-*\n");
|
if (assoc["mapped"] != "Is mapped") $stop;
|
||||||
$finish;
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@ module t (clk);
|
||||||
real v_arr_real [2];
|
real v_arr_real [2];
|
||||||
string v_string;
|
string v_string;
|
||||||
|
|
||||||
|
string v_assoc[string];
|
||||||
|
initial v_assoc["key"] = "value";
|
||||||
|
|
||||||
typedef struct packed {
|
typedef struct packed {
|
||||||
logic [31:0] data;
|
logic [31:0] data;
|
||||||
} str32_t;
|
} str32_t;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue