mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
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
|
||||
|
||||
** Support associative arrays (excluding [*] and pattern assignments).
|
||||
|
||||
*** Add +verilator+error+limit to see more assertion errors. [Peter Monsson]
|
||||
|
||||
*** 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
|
||||
|
||||
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 out = ld;
|
||||
for (std::string::iterator it = out.begin(); it != out.end(); ++it) {
|
||||
|
@ -30,8 +30,158 @@
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
//IFDEF C11
|
||||
#include <array>
|
||||
|
||||
#include <map>
|
||||
#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
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#define _VERILATED_SAVE_C_H_ 1
|
||||
|
||||
#include "verilatedos.h"
|
||||
#include "verilated_heavy.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@ -247,5 +248,33 @@ inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& r
|
||||
rhs.resize(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
|
||||
|
@ -1199,6 +1199,10 @@ AstBasicDType* AstNode::findInsertSameDType(AstBasicDType* nodep) {
|
||||
return v3Global.rootp()->typeTablep()
|
||||
->findInsertSameDType(nodep);
|
||||
}
|
||||
AstNodeDType* AstNode::findVoidDType() const {
|
||||
return v3Global.rootp()->typeTablep()
|
||||
->findVoidDType(fileline());
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// AstNVisitor
|
||||
|
@ -1421,6 +1421,7 @@ public:
|
||||
void dtypeSetSigned32() { dtypep(findSigned32DType()); }
|
||||
void dtypeSetUInt32() { dtypep(findUInt32DType()); } // Twostate
|
||||
void dtypeSetUInt64() { dtypep(findUInt64DType()); } // Twostate
|
||||
void dtypeSetVoid() { dtypep(findVoidDType()); }
|
||||
|
||||
// Data type locators
|
||||
AstNodeDType* findLogicBoolDType() { return findBasicDType(AstBasicDTypeKwd::LOGIC); }
|
||||
@ -1429,6 +1430,7 @@ public:
|
||||
AstNodeDType* findSigned32DType() { return findBasicDType(AstBasicDTypeKwd::INTEGER); }
|
||||
AstNodeDType* findUInt32DType() { return findBasicDType(AstBasicDTypeKwd::UINT32); } // Twostate
|
||||
AstNodeDType* findUInt64DType() { return findBasicDType(AstBasicDTypeKwd::UINT64); } // Twostate
|
||||
AstNodeDType* findVoidDType() const;
|
||||
AstNodeDType* findBitDType(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;
|
||||
@ -1914,6 +1916,8 @@ public:
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
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 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 AstNodeDType* subDTypep() const { return NULL; } // Iff has a non-null subDTypep(), as generic node function
|
||||
virtual bool isFourstate() const;
|
||||
|
@ -247,7 +247,7 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
|
||||
string ostatic;
|
||||
if (isStatic() && namespc.empty()) ostatic = "static ";
|
||||
|
||||
VlArgTypeRecursed info = vlArgTypeRecurse(forFunc, dtypep());
|
||||
VlArgTypeRecursed info = vlArgTypeRecurse(forFunc, dtypep(), false);
|
||||
|
||||
string oname;
|
||||
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;
|
||||
}
|
||||
|
||||
AstVar::VlArgTypeRecursed AstVar::vlArgTypeRecurse(bool forFunc,
|
||||
const AstNodeDType* dtypep) const {
|
||||
AstVar::VlArgTypeRecursed AstVar::vlArgTypeRecurse(bool forFunc, const AstNodeDType* dtypep,
|
||||
bool arrayed) const {
|
||||
dtypep = dtypep->skipRefp();
|
||||
if (const AstUnpackArrayDType* adtypep = VN_CAST_CONST(dtypep, UnpackArrayDType)) {
|
||||
VlArgTypeRecursed info = vlArgTypeRecurse(forFunc, adtypep->subDTypep());
|
||||
if (const AstAssocArrayDType* adtypep = VN_CAST_CONST(dtypep, AssocArrayDType)) {
|
||||
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;
|
||||
return info;
|
||||
} else if (const AstBasicDType* bdtypep = dtypep->basicp()) {
|
||||
@ -297,8 +314,12 @@ AstVar::VlArgTypeRecursed AstVar::vlArgTypeRecurse(bool forFunc,
|
||||
} else if (dtypep->isQuad()) {
|
||||
otype += "QData"+bitvec;
|
||||
} else if (dtypep->isWide()) {
|
||||
otype += "WData"+bitvec; // []'s added later
|
||||
oarray += "["+cvtToStr(dtypep->widthWords())+"]";
|
||||
if (arrayed) {
|
||||
otype += "VlWide<"+cvtToStr(dtypep->widthWords())+">";
|
||||
} else {
|
||||
otype += "WData"+bitvec; // []'s added later
|
||||
oarray += "["+cvtToStr(dtypep->widthWords())+"]";
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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.
|
||||
}
|
||||
void AstAssocArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str<<"[assoc-"<<(void*)keyDTypep()<<"]";
|
||||
}
|
||||
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str<<"[]";
|
||||
}
|
||||
void AstVoidDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str<<"void";
|
||||
}
|
||||
void AstVarScope::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
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; }
|
||||
};
|
||||
|
||||
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 {
|
||||
// Unsized range specification, for open arrays
|
||||
public:
|
||||
@ -333,6 +349,65 @@ public:
|
||||
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 {
|
||||
// Packed array data type, ie "some_dtype [2:0] var_name"
|
||||
// Children: DTYPE (moved to refDTypep() in V3Width)
|
||||
@ -774,6 +849,28 @@ public:
|
||||
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 {
|
||||
private:
|
||||
string m_name;
|
||||
@ -927,6 +1024,40 @@ public:
|
||||
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 {
|
||||
// Select a single word from a multi-word wide value
|
||||
public:
|
||||
@ -1119,7 +1250,7 @@ class AstMethodCall : public AstNode {
|
||||
// We do not support generic member calls yet, so this is only enough to
|
||||
// make built-in methods work
|
||||
private:
|
||||
string m_name; // Name of variable
|
||||
string m_name; // Name of method
|
||||
public:
|
||||
AstMethodCall(FileLine* fl, AstNode* fromp, VFlagChildDType, const string& name, AstNode* pinsp)
|
||||
: AstNode(fl), m_name(name) {
|
||||
@ -1141,6 +1272,45 @@ public:
|
||||
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 {
|
||||
// A variable (in/out/wire/reg/param) inside a module
|
||||
private:
|
||||
@ -1412,7 +1582,8 @@ public:
|
||||
string mtasksString() const;
|
||||
private:
|
||||
class VlArgTypeRecursed;
|
||||
VlArgTypeRecursed vlArgTypeRecurse(bool forFunc, const AstNodeDType* dtypep) const;
|
||||
VlArgTypeRecursed vlArgTypeRecurse(bool forFunc, const AstNodeDType* dtypep,
|
||||
bool arrayed) const;
|
||||
};
|
||||
|
||||
class AstDefParam : public AstNode {
|
||||
@ -6333,17 +6504,19 @@ public:
|
||||
class AstTypeTable : public AstNode {
|
||||
// Container for hash of standard data types
|
||||
// Children: NODEDTYPEs
|
||||
AstVoidDType* m_voidp;
|
||||
AstBasicDType* m_basicps[AstBasicDTypeKwd::_ENUM_MAX];
|
||||
//
|
||||
typedef std::map<VBasicTypeKey,AstBasicDType*> DetailedMap;
|
||||
DetailedMap m_detailedMap;
|
||||
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;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(TypeTable)
|
||||
AstNodeDType* typesp() const { return VN_CAST(op1p(), NodeDType);} // op1 = List of dtypes
|
||||
void addTypesp(AstNodeDType* nodep) { addOp1p(nodep); }
|
||||
AstVoidDType* findVoidDType(FileLine* fl);
|
||||
AstBasicDType* findBasicDType(FileLine* fl, AstBasicDTypeKwd kwd);
|
||||
AstBasicDType* findLogicBitDType(FileLine* fl, AstBasicDTypeKwd kwd,
|
||||
int width, int widthMin, AstNumeric numeric);
|
||||
|
@ -87,7 +87,9 @@ private:
|
||||
void computeCppWidth(AstNode* nodep) {
|
||||
if (!nodep->user2() && nodep->hasDType()) {
|
||||
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 {
|
||||
setCppWidth(nodep);
|
||||
}
|
||||
@ -276,6 +278,11 @@ private:
|
||||
insureCleanAndNext(nodep->argsp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
virtual void visit(AstCMethodCall* nodep) {
|
||||
iterateChildren(nodep);
|
||||
insureCleanAndNext(nodep->pinsp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
|
||||
//--------------------
|
||||
// Default: Just iterate
|
||||
|
@ -127,6 +127,9 @@ private:
|
||||
if (AstNode* subnodep = nodep->virtRefDTypep()) {
|
||||
subnodep->user1Inc();
|
||||
}
|
||||
if (AstNode* subnodep = nodep->virtRefDType2p()) {
|
||||
subnodep->user1Inc();
|
||||
}
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
|
@ -191,7 +191,9 @@ public:
|
||||
} else if (nodep->isWide()
|
||||
&& VN_IS(nodep->lhsp(), VarRef)
|
||||
&& !VN_IS(nodep->rhsp(), CMath)
|
||||
&& !VN_IS(nodep->rhsp(), CMethodCall)
|
||||
&& !VN_IS(nodep->rhsp(), VarRef)
|
||||
&& !VN_IS(nodep->rhsp(), AssocSel)
|
||||
&& !VN_IS(nodep->rhsp(), ArraySel)) {
|
||||
// Wide functions assign into the array directly, don't need separate assign statement
|
||||
m_wideTempRefp = VN_CAST(nodep->lhsp(), VarRef);
|
||||
@ -215,6 +217,27 @@ public:
|
||||
}
|
||||
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) {
|
||||
puts(nodep->hiernameProtect());
|
||||
puts(nodep->funcp()->nameProtect());
|
||||
@ -233,6 +256,23 @@ public:
|
||||
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) {
|
||||
// In V3Case...
|
||||
nodep->v3fatalSrc("Case statements should have been reduced out");
|
||||
@ -1303,7 +1343,12 @@ class EmitCImp : EmitCStmts {
|
||||
dtypep = dtypep->skipRefp();
|
||||
AstBasicDType* basicp = dtypep->basicp();
|
||||
// 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,
|
||||
"Should have swapped msb & lsb earlier.");
|
||||
string ivar = string("__Vi")+cvtToStr(depth);
|
||||
|
@ -46,6 +46,10 @@ class EmitCInlines : EmitCBaseVisitor {
|
||||
v3Global.needHeavy(true);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstAssocArrayDType* nodep) {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstValuePlusArgs* nodep) {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
|
@ -122,6 +122,11 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep,
|
||||
} else if (VN_IS(nrangep, UnsizedRange)) {
|
||||
arrayp = new AstUnsizedArrayDType
|
||||
(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 {
|
||||
UASSERT_OBJ(0, nrangep, "Expected range or unsized range");
|
||||
}
|
||||
|
@ -349,6 +349,16 @@ private:
|
||||
}
|
||||
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) {
|
||||
iterateChildren(nodep); checkNode(nodep);
|
||||
}
|
||||
|
@ -162,6 +162,7 @@ private:
|
||||
// Of a constant index
|
||||
AstConst* lbitp = VN_CAST(lselp->bitp(), Const);
|
||||
if (!lbitp) { mergeEnd(); return; }
|
||||
if (lbitp->width() > 32) { mergeEnd(); return; } // Assoc arrays can do this
|
||||
uint32_t index = lbitp->toUInt();
|
||||
// Of variable
|
||||
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) {
|
||||
// Always creates as output an unpacked array
|
||||
if (m_vup->prelim()) {
|
||||
@ -1052,6 +1066,16 @@ private:
|
||||
}
|
||||
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) {
|
||||
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
|
||||
if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep));
|
||||
@ -1589,6 +1613,7 @@ private:
|
||||
}
|
||||
}
|
||||
else if (VN_IS(fromDtp, EnumDType)
|
||||
|| VN_IS(fromDtp, AssocArrayDType)
|
||||
|| VN_IS(fromDtp, BasicDType)) {
|
||||
// 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
|
||||
@ -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) {
|
||||
UINFO(5," METHODSEL "<<nodep<<endl);
|
||||
if (nodep->didWidth()) return;
|
||||
@ -1648,6 +1678,9 @@ private:
|
||||
if (AstEnumDType* adtypep = VN_CAST(fromDtp, EnumDType)) {
|
||||
methodCallEnum(nodep, adtypep);
|
||||
}
|
||||
else if (AstAssocArrayDType* adtypep = VN_CAST(fromDtp, AssocArrayDType)) {
|
||||
methodCallAssoc(nodep, adtypep);
|
||||
}
|
||||
else if (AstUnpackArrayDType* adtypep = VN_CAST(fromDtp, UnpackArrayDType)) {
|
||||
methodCallUnpack(nodep, adtypep);
|
||||
}
|
||||
@ -1765,6 +1798,83 @@ private:
|
||||
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) {
|
||||
enum { UNKNOWN = 0, ARRAY_OR, ARRAY_AND, ARRAY_XOR } methodId;
|
||||
|
||||
@ -2314,13 +2424,25 @@ private:
|
||||
break;
|
||||
}
|
||||
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()) {
|
||||
added = true;
|
||||
newFormat += "\"%@\"";
|
||||
} else if (basicp && basicp->isDouble()) {
|
||||
added = true;
|
||||
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 {
|
||||
added = true;
|
||||
if (fmt == "%0") newFormat += "'h%0h"; // IEEE our choice
|
||||
|
@ -151,6 +151,7 @@ private:
|
||||
if (nodep->numeric().isNosign()) nodep->numeric(AstNumeric::UNSIGNED);
|
||||
iterateChildren(nodep);
|
||||
nodep->virtRefDTypep(editOneDType(nodep->virtRefDTypep()));
|
||||
nodep->virtRefDType2p(editOneDType(nodep->virtRefDType2p()));
|
||||
}
|
||||
virtual void visit(AstNodePreSel* nodep) { // LCOV_EXCL_LINE
|
||||
// This check could go anywhere after V3Param
|
||||
|
@ -91,6 +91,8 @@ private:
|
||||
if (const AstNodeArrayDType* adtypep = VN_CAST(ddtypep, NodeArrayDType)) {
|
||||
fromRange = adtypep->declRange();
|
||||
}
|
||||
else if (const AstAssocArrayDType* adtypep = VN_CAST(ddtypep, AssocArrayDType)) {
|
||||
}
|
||||
else if (const AstNodeClassDType* adtypep = VN_CAST(ddtypep, NodeClassDType)) {
|
||||
fromRange = adtypep->declRange();
|
||||
}
|
||||
@ -246,6 +248,15 @@ private:
|
||||
if (debug()>=9) newp->dumpTree(cout, "--SELBTn: ");
|
||||
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)) {
|
||||
// SELBIT(range, index) -> SEL(array, index, 1)
|
||||
AstSel* newp = new AstSel(nodep->fileline(),
|
||||
|
@ -1627,9 +1627,9 @@ variable_dimension<rangep>: // ==IEEE: variable_dimension
|
||||
| anyrange { $$ = $1; }
|
||||
| '[' constExpr ']' { $$ = new AstRange($1, new AstConst($1, 0), new AstSub($1, $2, new AstConst($1, 1))); }
|
||||
// // IEEE: associative_dimension
|
||||
//UNSUP '[' data_type ']' { UNSUP }
|
||||
//UNSUP yP_BRASTAR ']' { UNSUP }
|
||||
//UNSUP '[' '*' ']' { UNSUP }
|
||||
| '[' data_type ']' { $$ = new AstAssocRange($1, $2); }
|
||||
| yP_BRASTAR ']' { $$ = NULL; v3error("Unsupported: [*] wildcard associative arrays"); }
|
||||
| '[' '*' ']' { $$ = NULL; v3error("Unsupported: [*] wildcard associative arrays"); }
|
||||
// // IEEE: queue_dimension
|
||||
// // '[' '$' ']' -- $ is part of expr
|
||||
// // '[' '$' ':' expr ']' -- anyrange:expr:$
|
||||
|
20
test_regress/t/t_assoc.pl
Executable file
20
test_regress/t/t_assoc.pl
Executable file
@ -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;
|
104
test_regress/t/t_assoc.v
Normal file
104
test_regress/t/t_assoc.v
Normal file
@ -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
|
37
test_regress/t/t_assoc_meth_bad.out
Normal file
37
test_regress/t/t_assoc_meth_bad.out
Normal file
@ -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
|
18
test_regress/t/t_assoc_meth_bad.pl
Executable file
18
test_regress/t/t_assoc_meth_bad.pl
Executable file
@ -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;
|
23
test_regress/t/t_assoc_meth_bad.v
Normal file
23
test_regress/t/t_assoc_meth_bad.v
Normal file
@ -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
|
5
test_regress/t/t_assoc_pattern_unsup.out
Normal file
5
test_regress/t/t_assoc_pattern_unsup.out
Normal file
@ -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
|
18
test_regress/t/t_assoc_pattern_unsup.pl
Executable file
18
test_regress/t/t_assoc_pattern_unsup.pl
Executable file
@ -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;
|
27
test_regress/t/t_assoc_pattern_unsup.v
Normal file
27
test_regress/t/t_assoc_pattern_unsup.v
Normal file
@ -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
|
2
test_regress/t/t_assoc_wildcard_unsup.out
Normal file
2
test_regress/t/t_assoc_wildcard_unsup.out
Normal file
@ -0,0 +1,2 @@
|
||||
%Error: Unsupported: [*] wildcard associative arrays
|
||||
%Error: Exiting due to
|
18
test_regress/t/t_assoc_wildcard_unsup.pl
Executable file
18
test_regress/t/t_assoc_wildcard_unsup.pl
Executable file
@ -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;
|
49
test_regress/t/t_assoc_wildcard_unsup.v
Normal file
49
test_regress/t/t_assoc_wildcard_unsup.v
Normal file
@ -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;
|
||||
string s,s2;
|
||||
string sarr[2:1];
|
||||
string assoc[string];
|
||||
|
||||
string si;
|
||||
|
||||
@ -63,6 +64,7 @@ module sub (/*AUTOARG*/
|
||||
s <= "hello";
|
||||
sarr[1] <= "sarr[1]";
|
||||
sarr[2] <= "sarr[2]";
|
||||
assoc["mapped"] <= "Is mapped";
|
||||
end
|
||||
if (cyc==1) begin
|
||||
if ($test$plusargs("save_restore")!=0) begin
|
||||
@ -88,8 +90,9 @@ module sub (/*AUTOARG*/
|
||||
$display("%s",s);
|
||||
$display("%s",sarr[1]);
|
||||
$display("%s",sarr[2]);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
if (assoc["mapped"] != "Is mapped") $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
@ -49,6 +49,9 @@ module t (clk);
|
||||
real v_arr_real [2];
|
||||
string v_string;
|
||||
|
||||
string v_assoc[string];
|
||||
initial v_assoc["key"] = "value";
|
||||
|
||||
typedef struct packed {
|
||||
logic [31:0] data;
|
||||
} str32_t;
|
||||
|
Loading…
Reference in New Issue
Block a user