Support associative arrays.

This commit is contained in:
Wilson Snyder 2019-12-01 11:52:48 -05:00
parent 6ae7f7b152
commit b81295230a
32 changed files with 967 additions and 18 deletions

View File

@ -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.

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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]";

View File

@ -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);

View File

@ -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

View File

@ -127,6 +127,9 @@ private:
if (AstNode* subnodep = nodep->virtRefDTypep()) {
subnodep->user1Inc();
}
if (AstNode* subnodep = nodep->virtRefDType2p()) {
subnodep->user1Inc();
}
}
// VISITORS

View File

@ -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);

View File

@ -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);

View File

@ -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");
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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(),

View File

@ -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
View 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
View 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

View 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

View 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;

View 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

View 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

View 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;

View 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

View File

@ -0,0 +1,2 @@
%Error: Unsupported: [*] wildcard associative arrays
%Error: Exiting due to

View 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;

View 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

View File

@ -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

View File

@ -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;