forked from github/verilator
Support queues (excluding {} notation and pattern assignments), bug545.
This commit is contained in:
parent
b81295230a
commit
e28175108f
4
Changes
4
Changes
@ -4,7 +4,9 @@ The contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
* Verilator 4.023 devel
|
||||
|
||||
** Support associative arrays (excluding [*] and pattern assignments).
|
||||
** Support associative arrays (excluding [*] and pattern assignments), bug544.
|
||||
|
||||
** Support queues (excluding {} notation and pattern assignments), bug545.
|
||||
|
||||
*** Add +verilator+error+limit to see more assertion errors. [Peter Monsson]
|
||||
|
||||
|
@ -30,9 +30,7 @@
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
//IFDEF C11
|
||||
#include <array>
|
||||
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
@ -182,6 +180,99 @@ std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog queue container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
//
|
||||
template <class T_Value> class VlQueue {
|
||||
private:
|
||||
// TYPES
|
||||
typedef std::deque<T_Value> Deque;
|
||||
public:
|
||||
typedef typename Deque::const_iterator const_iterator;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
Deque m_deque; // State of the assoc array
|
||||
T_Value m_defaultValue; // Default value
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlQueue() {
|
||||
// m_defaultValue isn't defaulted. Caller's constructor must do it.
|
||||
}
|
||||
~VlQueue() {}
|
||||
// Standard copy constructor works. Verilog: assoca = assocb
|
||||
|
||||
// METHODS
|
||||
T_Value& atDefault() { return m_defaultValue; }
|
||||
|
||||
// Size. Verilog: function int size(), or int num()
|
||||
int size() const { return m_deque.size(); }
|
||||
// Clear array. Verilog: function void delete([input index])
|
||||
void clear() { m_deque.clear(); }
|
||||
void erase(size_t index) { if (VL_LIKELY(index < m_deque.size())) m_deque.erase(index); }
|
||||
|
||||
// function void q.push_front(value)
|
||||
void push_front(const T_Value& value) { m_deque.push_front(value); }
|
||||
// function void q.push_back(value)
|
||||
void push_back(const T_Value& value) { m_deque.push_back(value); }
|
||||
// function value_t q.pop_front();
|
||||
const T_Value& pop_front() {
|
||||
if (m_deque.empty()) return m_defaultValue;
|
||||
const T_Value& v = m_deque.front(); m_deque.pop_front(); return v;
|
||||
}
|
||||
// function value_t q.pop_back();
|
||||
const T_Value& pop_back() {
|
||||
if (m_deque.empty()) return m_defaultValue;
|
||||
const T_Value& v = m_deque.back(); m_deque.pop_back(); return v;
|
||||
}
|
||||
|
||||
// Setting. Verilog: assoc[index] = v
|
||||
// Can't just overload operator[] or provide a "at" reference to set,
|
||||
// because we need to be able to insert only when the value is set
|
||||
T_Value& at(size_t index) {
|
||||
static T_Value s_throwAway;
|
||||
if (VL_UNLIKELY(index >= m_deque.size())) {
|
||||
s_throwAway = atDefault();
|
||||
return s_throwAway;
|
||||
}
|
||||
else return m_deque[index];
|
||||
}
|
||||
// Accessing. Verilog: v = assoc[index]
|
||||
const T_Value& at(size_t index) const {
|
||||
static T_Value s_throwAway;
|
||||
if (VL_UNLIKELY(index >= m_deque.size())) return atDefault();
|
||||
else return m_deque[index];
|
||||
}
|
||||
// function void q.insert(index, value);
|
||||
void insert(size_t index, const T_Value& value) {
|
||||
if (VL_UNLIKELY(index >= m_deque.size())) return;
|
||||
m_deque[index] = value;
|
||||
}
|
||||
|
||||
// For save/restore
|
||||
const_iterator begin() const { return m_deque.begin(); }
|
||||
const_iterator end() const { return m_deque.end(); }
|
||||
|
||||
// Dumping. Verilog: str = $sformatf("%p", assoc)
|
||||
std::string to_string() const {
|
||||
std::string out = "'{";
|
||||
std::string comma;
|
||||
for (typename Deque::const_iterator it = m_deque.begin(); it != m_deque.end(); ++it) {
|
||||
out += comma + VL_TO_STRING(*it);
|
||||
comma = ", ";
|
||||
}
|
||||
return out + "} ";
|
||||
}
|
||||
};
|
||||
|
||||
template <class T_Value>
|
||||
std::string VL_TO_STRING(const VlQueue<T_Value>& obj) {
|
||||
return obj.to_string();
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Conversion functions
|
||||
|
||||
|
@ -278,6 +278,16 @@ AstVar::VlArgTypeRecursed AstVar::vlArgTypeRecurse(bool forFunc, const AstNodeDT
|
||||
VlArgTypeRecursed info;
|
||||
info.m_oprefix = out;
|
||||
return info;
|
||||
} else if (const AstQueueDType* adtypep = VN_CAST_CONST(dtypep, QueueDType)) {
|
||||
VlArgTypeRecursed sub = vlArgTypeRecurse(forFunc, adtypep->subDTypep(), true);
|
||||
VlArgTypeRecursed info;
|
||||
string out = "VlQueue<" + sub.m_oprefix;
|
||||
if (!sub.m_osuffix.empty() || !sub.m_oref.empty()) {
|
||||
out += " " + sub.m_osuffix + sub.m_oref;
|
||||
}
|
||||
out += ">";
|
||||
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;
|
||||
@ -1109,6 +1119,10 @@ void AstAssocArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str<<"[assoc-"<<(void*)keyDTypep()<<"]";
|
||||
}
|
||||
void AstQueueDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str<<"[queue]";
|
||||
}
|
||||
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str<<"[]";
|
||||
|
@ -200,6 +200,19 @@ public:
|
||||
AstNodeDType* keyDTypep() const { return VN_CAST(op1p(), NodeDType); }
|
||||
};
|
||||
|
||||
class AstQueueRange : public AstNodeRange {
|
||||
// Queue range specification
|
||||
// Only for early parsing - becomes AstQueueDType
|
||||
public:
|
||||
explicit AstQueueRange(FileLine* fl)
|
||||
: AstNodeRange(fl) {}
|
||||
ASTNODE_NODE_FUNCS(QueueRange)
|
||||
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; }
|
||||
};
|
||||
|
||||
class AstUnsizedRange : public AstNodeRange {
|
||||
// Unsized range specification, for open arrays
|
||||
public:
|
||||
@ -717,6 +730,50 @@ public:
|
||||
bool isModport() { return !m_modportName.empty(); }
|
||||
};
|
||||
|
||||
class AstQueueDType : public AstNodeDType {
|
||||
// Queue array data type, ie "[ $ ]"
|
||||
// Children: DTYPE (moved to refDTypep() in V3Width)
|
||||
private:
|
||||
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
|
||||
public:
|
||||
AstQueueDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp)
|
||||
: AstNodeDType(fl) {
|
||||
childDTypep(dtp); // Only for parser
|
||||
refDTypep(NULL);
|
||||
dtypep(NULL); // V3Width will resolve
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(QueueDType)
|
||||
virtual const char* broken() const {
|
||||
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||
|| (!m_refDTypep && childDTypep())));
|
||||
return NULL; }
|
||||
virtual void cloneRelink() {
|
||||
if (m_refDTypep && m_refDTypep->clonep()) { m_refDTypep = m_refDTypep->clonep(); } }
|
||||
virtual bool same(const AstNode* samep) const {
|
||||
const AstQueueDType* asamep = static_cast<const AstQueueDType*>(samep);
|
||||
return (subDTypep() == asamep->subDTypep()); }
|
||||
virtual bool similarDType(AstNodeDType* samep) const {
|
||||
const AstQueueDType* asamep = static_cast<const AstQueueDType*>(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); }
|
||||
// METHODS
|
||||
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 subDTypep()->widthAlignBytes(); }
|
||||
virtual int widthTotalBytes() const { return subDTypep()->widthTotalBytes(); }
|
||||
};
|
||||
|
||||
class AstRefDType : public AstNodeDType {
|
||||
private:
|
||||
AstNodeDType* m_refDTypep; // data type pointed to, BELOW the AstTypedef
|
||||
@ -2247,6 +2304,16 @@ public:
|
||||
AstNode* rhsp() const { return op2p(); }
|
||||
};
|
||||
|
||||
class AstUnbounded : public AstNode {
|
||||
// A $ in the parser, used for unbounded and queues
|
||||
public:
|
||||
AstUnbounded(FileLine* fl)
|
||||
: AstNode(fl) {}
|
||||
ASTNODE_NODE_FUNCS(Unbounded)
|
||||
virtual string emitVerilog() { return "$"; }
|
||||
virtual string emitC() { V3ERROR_NA; return ""; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
class AstTask : public AstNodeFTask {
|
||||
|
@ -88,6 +88,7 @@ private:
|
||||
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(), AssocArrayDType) // Or arrays
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)
|
||||
|| VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) {
|
||||
} else {
|
||||
|
@ -1348,6 +1348,9 @@ class EmitCImp : EmitCStmts {
|
||||
return emitVarResetRecurse(varp, adtypep->subDTypep(), depth+1,
|
||||
".atDefault()" + cvtarray);
|
||||
}
|
||||
else if (AstQueueDType* adtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
return emitVarResetRecurse(varp, adtypep->subDTypep(), depth+1, ".atDefault()");
|
||||
}
|
||||
else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
UASSERT_OBJ(adtypep->msb() >= adtypep->lsb(), varp,
|
||||
"Should have swapped msb & lsb earlier.");
|
||||
|
@ -50,6 +50,10 @@ class EmitCInlines : EmitCBaseVisitor {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstQueueDType* nodep) {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
virtual void visit(AstValuePlusArgs* nodep) {
|
||||
v3Global.needHeavy(true);
|
||||
iterateChildren(nodep);
|
||||
|
@ -117,6 +117,10 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep,
|
||||
arrayp = new AstPackArrayDType
|
||||
(rangep->fileline(), VFlagChildDType(), arrayp, rangep);
|
||||
} else if (rangep) {
|
||||
if (VN_IS(rangep->leftp(), Unbounded)
|
||||
|| VN_IS(rangep->rightp(), Unbounded)) {
|
||||
rangep->v3error("Unsupported: Bounded queues. Suggest use unbounded.");
|
||||
}
|
||||
arrayp = new AstUnpackArrayDType
|
||||
(rangep->fileline(), VFlagChildDType(), arrayp, rangep);
|
||||
} else if (VN_IS(nrangep, UnsizedRange)) {
|
||||
@ -127,6 +131,9 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep,
|
||||
AstNodeDType* keyp = arangep->keyDTypep(); keyp->unlinkFrBack();
|
||||
arrayp = new AstAssocArrayDType
|
||||
(nrangep->fileline(), VFlagChildDType(), arrayp, keyp);
|
||||
} else if (VN_IS(nrangep, QueueRange)) {
|
||||
arrayp = new AstQueueDType
|
||||
(nrangep->fileline(), VFlagChildDType(), arrayp);
|
||||
} else {
|
||||
UASSERT_OBJ(0, nrangep, "Expected range or unsized range");
|
||||
}
|
||||
|
108
src/V3Width.cpp
108
src/V3Width.cpp
@ -880,6 +880,9 @@ private:
|
||||
nodep->dtypeSetSigned32(); // Says the spec
|
||||
}
|
||||
}
|
||||
virtual void visit(AstUnbounded* nodep) {
|
||||
nodep->v3error("Unsupported/illegal unbounded ('$') in this context.");
|
||||
}
|
||||
virtual void visit(AstUCFunc* nodep) {
|
||||
// Give it the size the user wants.
|
||||
if (m_vup && m_vup->prelim()) {
|
||||
@ -1076,6 +1079,14 @@ private:
|
||||
nodep->dtypep(nodep); // The array itself, not subDtype
|
||||
UINFO(4,"dtWidthed "<<nodep<<endl);
|
||||
}
|
||||
virtual void visit(AstQueueDType* nodep) {
|
||||
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
|
||||
if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep));
|
||||
// Iterate into subDTypep() to resolve that type and update pointer.
|
||||
nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep()));
|
||||
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));
|
||||
@ -1614,6 +1625,7 @@ private:
|
||||
}
|
||||
else if (VN_IS(fromDtp, EnumDType)
|
||||
|| VN_IS(fromDtp, AssocArrayDType)
|
||||
|| VN_IS(fromDtp, QueueDType)
|
||||
|| 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
|
||||
@ -1681,6 +1693,9 @@ private:
|
||||
else if (AstAssocArrayDType* adtypep = VN_CAST(fromDtp, AssocArrayDType)) {
|
||||
methodCallAssoc(nodep, adtypep);
|
||||
}
|
||||
else if (AstQueueDType* adtypep = VN_CAST(fromDtp, QueueDType)) {
|
||||
methodCallQueue(nodep, adtypep);
|
||||
}
|
||||
else if (AstUnpackArrayDType* adtypep = VN_CAST(fromDtp, UnpackArrayDType)) {
|
||||
methodCallUnpack(nodep, adtypep);
|
||||
}
|
||||
@ -1875,6 +1890,93 @@ private:
|
||||
if (lvalue) varrefp->lvalue(true);
|
||||
}
|
||||
}
|
||||
void methodCallQueue(AstMethodCall* nodep, AstQueueDType* adtypep) {
|
||||
AstCMethodCall* newp = NULL;
|
||||
if (nodep->name() == "at") { // Created internally for []
|
||||
methodOkArguments(nodep, 1, 1);
|
||||
methodCallLValue(nodep, nodep->fromp(), true);
|
||||
newp = new AstCMethodCall(nodep->fileline(),
|
||||
nodep->fromp()->unlinkFrBack(),
|
||||
"at", NULL);
|
||||
newp->dtypeFrom(adtypep->subDTypep());
|
||||
newp->protect(false);
|
||||
newp->didWidth(true);
|
||||
} else if (nodep->name() == "num" // function int num()
|
||||
|| nodep->name() == "size") {
|
||||
methodOkArguments(nodep, 0, 0);
|
||||
newp = new AstCMethodCall(nodep->fileline(),
|
||||
nodep->fromp()->unlinkFrBack(),
|
||||
"size", NULL);
|
||||
newp->dtypeSetSigned32();
|
||||
newp->didWidth(true);
|
||||
newp->protect(false);
|
||||
} 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 {
|
||||
nodep->v3error("Unsupported: Queue .delete(index) method, as is O(n) complexity and slow.");
|
||||
AstNode* index_exprp = methodCallQueueIndexExpr(nodep);
|
||||
newp = new AstCMethodCall(nodep->fileline(),
|
||||
nodep->fromp()->unlinkFrBack(),
|
||||
"erase", index_exprp->unlinkFrBack());
|
||||
newp->protect(false);
|
||||
newp->makeStatement();
|
||||
}
|
||||
} else if (nodep->name() == "insert") {
|
||||
nodep->v3error("Unsupported: Queue .insert method, as is O(n) complexity and slow.");
|
||||
methodOkArguments(nodep, 2, 2);
|
||||
methodCallLValue(nodep, nodep->fromp(), true);
|
||||
AstNode* index_exprp = methodCallQueueIndexExpr(nodep);
|
||||
AstArg* argp = VN_CAST(nodep->pinsp()->nextp(), Arg);
|
||||
iterateCheckTyped(nodep, "insert value", argp->exprp(), adtypep->subDTypep(), BOTH);
|
||||
newp = new AstCMethodCall(nodep->fileline(),
|
||||
nodep->fromp()->unlinkFrBack(),
|
||||
nodep->name(),
|
||||
index_exprp->unlinkFrBack());
|
||||
newp->addPinsp(argp->exprp()->unlinkFrBack());
|
||||
newp->protect(false);
|
||||
newp->makeStatement();
|
||||
} else if (nodep->name() == "pop_front"
|
||||
|| nodep->name() == "pop_back") {
|
||||
methodOkArguments(nodep, 0, 0);
|
||||
methodCallLValue(nodep, nodep->fromp(), true);
|
||||
newp = new AstCMethodCall(nodep->fileline(),
|
||||
nodep->fromp()->unlinkFrBack(),
|
||||
nodep->name(), NULL);
|
||||
newp->dtypeFrom(adtypep->subDTypep());
|
||||
newp->protect(false);
|
||||
newp->didWidth(true);
|
||||
} else if (nodep->name() == "push_back"
|
||||
|| nodep->name() == "push_front") {
|
||||
methodOkArguments(nodep, 1, 1);
|
||||
methodCallLValue(nodep, nodep->fromp(), true);
|
||||
AstArg* argp = VN_CAST(nodep->pinsp(), Arg);
|
||||
iterateCheckTyped(nodep, "push value", argp->exprp(), adtypep->subDTypep(), BOTH);
|
||||
newp = new AstCMethodCall(nodep->fileline(),
|
||||
nodep->fromp()->unlinkFrBack(),
|
||||
nodep->name(), argp->exprp()->unlinkFrBack());
|
||||
newp->protect(false);
|
||||
newp->makeStatement();
|
||||
} else {
|
||||
nodep->v3error("Unsupported/unknown built-in associative array method "<<nodep->prettyNameQ());
|
||||
}
|
||||
if (newp) {
|
||||
newp->didWidth(true);
|
||||
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
}
|
||||
AstNode* methodCallQueueIndexExpr(AstMethodCall* nodep) {
|
||||
AstNode* index_exprp = VN_CAST(nodep->pinsp(), Arg)->exprp();
|
||||
iterateCheckSigned32(nodep, "index", index_exprp, BOTH);
|
||||
VL_DANGLING(index_exprp); // May have been edited
|
||||
return VN_CAST(nodep->pinsp(), Arg)->exprp();
|
||||
}
|
||||
void methodCallUnpack(AstMethodCall* nodep, AstUnpackArrayDType* adtypep) {
|
||||
enum { UNKNOWN = 0, ARRAY_OR, ARRAY_AND, ARRAY_XOR } methodId;
|
||||
|
||||
@ -2432,7 +2534,8 @@ private:
|
||||
} else if (basicp && basicp->isDouble()) {
|
||||
added = true;
|
||||
newFormat += "%g";
|
||||
} else if (VN_IS(dtypep, AssocArrayDType)) {
|
||||
} else if (VN_IS(dtypep, AssocArrayDType)
|
||||
|| VN_IS(dtypep, QueueDType)) {
|
||||
added = true;
|
||||
newFormat += "%@";
|
||||
AstNRelinker handle;
|
||||
@ -3881,7 +3984,8 @@ private:
|
||||
AstNode* spliceCvtString(AstNode* nodep) {
|
||||
// IEEE-2012 11.8.1: Signed: Type coercion creates signed
|
||||
// 11.8.2: Argument to convert is self-determined
|
||||
if (nodep && !nodep->dtypep()->basicp()->isString()) {
|
||||
if (nodep && !(nodep->dtypep()->basicp()
|
||||
&& nodep->dtypep()->basicp()->isString())) {
|
||||
UINFO(6," spliceCvtString: "<<nodep<<endl);
|
||||
AstNRelinker linker;
|
||||
nodep->unlinkFrBack(&linker);
|
||||
|
@ -93,6 +93,8 @@ private:
|
||||
}
|
||||
else if (const AstAssocArrayDType* adtypep = VN_CAST(ddtypep, AssocArrayDType)) {
|
||||
}
|
||||
else if (const AstQueueDType* adtypep = VN_CAST(ddtypep, QueueDType)) {
|
||||
}
|
||||
else if (const AstNodeClassDType* adtypep = VN_CAST(ddtypep, NodeClassDType)) {
|
||||
fromRange = adtypep->declRange();
|
||||
}
|
||||
@ -257,6 +259,15 @@ private:
|
||||
if (debug()>=9) newp->dumpTree(cout, "--SELBTn: ");
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
else if (AstQueueDType* adtypep = VN_CAST(ddtypep, QueueDType)) {
|
||||
// SELBIT(array, index) -> CMETHODCALL(queue, "at", index)
|
||||
AstNode* subp = rhsp;
|
||||
AstCMethodCall* newp = new AstCMethodCall(nodep->fileline(),
|
||||
fromp, "at", subp);
|
||||
newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off queue reference
|
||||
if (debug()>=9) newp->dumpTree(cout, "--SELBTq: ");
|
||||
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(),
|
||||
|
@ -1625,13 +1625,15 @@ variable_dimension<rangep>: // ==IEEE: variable_dimension
|
||||
'[' ']' { $$ = new AstUnsizedRange($1); }
|
||||
// // IEEE: unpacked_dimension
|
||||
| anyrange { $$ = $1; }
|
||||
| '[' constExpr ']' { $$ = new AstRange($1, new AstConst($1, 0), new AstSub($1, $2, new AstConst($1, 1))); }
|
||||
| '[' constExpr ']' { if (VN_IS($2, Unbounded)) { $2->deleteTree(); $$ = new AstQueueRange($1); }
|
||||
else { $$ = new AstRange($1, new AstConst($1, 0),
|
||||
new AstSub($1, $2, new AstConst($1, 1))); } }
|
||||
// // IEEE: associative_dimension
|
||||
| '[' 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
|
||||
// // '[' '$' ']' -- $ is part of expr, see '[' constExpr ']'
|
||||
// // '[' '$' ':' expr ']' -- anyrange:expr:$
|
||||
;
|
||||
|
||||
@ -3362,8 +3364,7 @@ expr<nodep>: // IEEE: part of expression/constant_expression/primary
|
||||
// // IEEE: sequence_method_call
|
||||
// // Indistinguishable from function_subroutine_call:method_call
|
||||
//
|
||||
| '$' { $$ = new AstConst($<fl>1, AstConst::LogicFalse());
|
||||
BBUNSUP($<fl>1, "Unsupported: $ expression"); }
|
||||
| '$' { $$ = new AstUnbounded($<fl>1); }
|
||||
| yNULL { $$ = new AstConst($1, AstConst::LogicFalse());
|
||||
BBUNSUP($<fl>1, "Unsupported: null expression"); }
|
||||
// // IEEE: yTHIS
|
||||
|
20
test_regress/t/t_queue.pl
Executable file
20
test_regress/t/t_queue.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;
|
83
test_regress/t/t_queue.v
Normal file
83
test_regress/t/t_queue.v
Normal file
@ -0,0 +1,83 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2019 by Wilson Snyder.
|
||||
|
||||
`define stop $stop
|
||||
`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
|
||||
// Simple test using integer
|
||||
typedef bit [3:0] nibble_t;
|
||||
nibble_t q[$];
|
||||
nibble_t v;
|
||||
|
||||
i = q.size(); `checkh(i, 0);
|
||||
q.push_back(4'd1); // 1
|
||||
q.push_front(4'd2); // 2 1
|
||||
q.push_back(4'd3); // 2 1 3
|
||||
i = q.size; `checkh(i, 3); // Also checks no parens
|
||||
end
|
||||
|
||||
begin
|
||||
// Strings
|
||||
string q[$];
|
||||
string v;
|
||||
|
||||
q.push_front("f1");
|
||||
q.push_back("b1");
|
||||
q.push_front("f2");
|
||||
q.push_back("b2");
|
||||
i = q.size(); `checkh(i, 4);
|
||||
|
||||
v = q[0]; `checks(v, "f2");
|
||||
v = q[1]; `checks(v, "f1");
|
||||
v = q[2]; `checks(v, "b1");
|
||||
v = q[3]; `checks(v, "b2");
|
||||
v = q[4]; `checks(v, "");
|
||||
|
||||
v = $sformatf("%p", q); `checks(v, "'{\"f2\", \"f1\", \"b1\", \"b2\"} ");
|
||||
|
||||
//Unsup: q.delete(1);
|
||||
//Unsup: v = q[1]; `checks(v, "b1");
|
||||
|
||||
//Unsup: q.insert(0, "ins0");
|
||||
//Unsup: q.insert(3, "ins3");
|
||||
//v = q[0]; `checks(v, "ins0");
|
||||
//v = q[3]; `checks(v, "ins3");
|
||||
|
||||
v = q.pop_front(); `checks(v, "f2");
|
||||
v = q.pop_front(); `checks(v, "f1");
|
||||
v = q.pop_back(); `checks(v, "b2");
|
||||
v = q.pop_back(); `checks(v, "b1");
|
||||
i = q.size(); `checkh(i, 0);
|
||||
|
||||
q.push_front("non-empty");
|
||||
i = q.size(); `checkh(i, 1);
|
||||
q.delete();
|
||||
i = q.size(); `checkh(i, 0);
|
||||
v = q.pop_front(); `checks(v, ""); // Was empty, optional warning
|
||||
v = q.pop_back(); `checks(v, ""); // Was empty, optional warning
|
||||
end
|
||||
|
||||
// See t_queue_unsup_bad for more unsupported stuff
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
4
test_regress/t/t_queue_bounded_unsup_bad.out
Normal file
4
test_regress/t/t_queue_bounded_unsup_bad.out
Normal file
@ -0,0 +1,4 @@
|
||||
%Error: t/t_queue_bounded_unsup_bad.v:7: Unsupported: Bounded queues. Suggest use unbounded.
|
||||
int q[$ : 3];
|
||||
^
|
||||
%Error: Exiting due to
|
18
test_regress/t/t_queue_bounded_unsup_bad.pl
Executable file
18
test_regress/t/t_queue_bounded_unsup_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 2003 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(linter => 1);
|
||||
|
||||
lint(
|
||||
fails => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
8
test_regress/t/t_queue_bounded_unsup_bad.v
Normal file
8
test_regress/t/t_queue_bounded_unsup_bad.v
Normal file
@ -0,0 +1,8 @@
|
||||
// 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*/);
|
||||
int q[$ : 3];
|
||||
endmodule
|
33
test_regress/t/t_queue_unsup_bad.out
Normal file
33
test_regress/t/t_queue_unsup_bad.out
Normal file
@ -0,0 +1,33 @@
|
||||
%Error: t/t_queue_unsup_bad.v:23: Unsupported: Queue .delete(index) method, as is O(n) complexity and slow.
|
||||
: ... In instance t
|
||||
q.delete(1);
|
||||
^~~~~~
|
||||
%Error: t/t_queue_unsup_bad.v:26: Unsupported: Queue .insert method, as is O(n) complexity and slow.
|
||||
: ... In instance t
|
||||
q.insert(0, "ins0");
|
||||
^~~~~~
|
||||
%Error: t/t_queue_unsup_bad.v:27: Unsupported: Queue .insert method, as is O(n) complexity and slow.
|
||||
: ... In instance t
|
||||
q.insert(2, "ins2");
|
||||
^~~~~~
|
||||
%Error: t/t_queue_unsup_bad.v:37: Unsupported/illegal unbounded ('$') in this context.
|
||||
: ... In instance t
|
||||
q = {q[0], q[2:$]};
|
||||
^
|
||||
%Error: t/t_queue_unsup_bad.v:37: Expecting expression to be constant, but can't convert a UNBOUNDED to constant.
|
||||
: ... In instance t
|
||||
q = {q[0], q[2:$]};
|
||||
^
|
||||
%Error: t/t_queue_unsup_bad.v:37: First value of [a:b] isn't a constant, maybe you want +: or -:
|
||||
: ... In instance t
|
||||
q = {q[0], q[2:$]};
|
||||
^
|
||||
%Error: t/t_queue_unsup_bad.v:37: Illegal range select; type already selected, or bad dimension: type is
|
||||
: ... In instance t
|
||||
q = {q[0], q[2:$]};
|
||||
^
|
||||
%Error: t/t_queue_unsup_bad.v:46: Unsupported: Assignment pattern applies against non struct/union: QUEUEDTYPE
|
||||
: ... In instance t
|
||||
q = '{ "BB", "CC" };
|
||||
^~
|
||||
%Error: Exiting due to
|
18
test_regress/t/t_queue_unsup_bad.pl
Executable file
18
test_regress/t/t_queue_unsup_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 2003 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);
|
||||
|
||||
compile(
|
||||
fails => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
75
test_regress/t/t_queue_unsup_bad.v
Normal file
75
test_regress/t/t_queue_unsup_bad.v
Normal file
@ -0,0 +1,75 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2019 by Wilson Snyder.
|
||||
|
||||
`define stop $stop
|
||||
`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
|
||||
string q[$];
|
||||
string v;
|
||||
int i;
|
||||
|
||||
q.push_front("non-empty");
|
||||
i = q.size(); `checkh(i, 0);
|
||||
|
||||
q = {"q", "b", "c", "d", "e", "f"};
|
||||
v = $sformatf("%p", q); `checks(v, "'{\"q\", \"b\", \"c\", \"d\", \"e\", \"f\"} ");
|
||||
|
||||
q.delete(1);
|
||||
v = q[1]; `checks(v, "c");
|
||||
|
||||
q.insert(0, "ins0");
|
||||
q.insert(2, "ins2");
|
||||
v = q[0]; `checks(v, "ins0");
|
||||
v = q[2]; `checks(v, "ins2");
|
||||
v = $sformatf("%p", q); `checks(v, "'{need_update, \"q\", \"b\", \"c\", \"d\", \"e\", \"f\"} ");
|
||||
|
||||
// Similar using implied notation
|
||||
q = {q, "f1"}; // push_front
|
||||
q = {q, "f2"}; // push_front
|
||||
q = {"b1", q}; // push_back
|
||||
q = {"b2", q}; // push_back
|
||||
q = {q[0], q[2:$]}; // delete element 1
|
||||
v = $sformatf("%p", q); `checks(v, "'{need_update, \"q\", \"b\", \"c\", \"d\", \"e\", \"f\"} ");
|
||||
|
||||
begin
|
||||
string ai[$] = { "Foo", "Bar" };
|
||||
q = ai; // Copy
|
||||
i = q.size(); `checkh(i, 2);
|
||||
v = q.pop_front(); `checks(v, "Foo");
|
||||
v = q.pop_front(); `checks(v, "Bar");
|
||||
q = '{ "BB", "CC" }; // Note '{} not {}
|
||||
v = q.pop_front(); `checks(v, "BB");
|
||||
v = q.pop_front(); `checks(v, "CC");
|
||||
q = { "BB", "CC" }; // Note {} not '{}
|
||||
v = q.pop_front(); `checks(v, "BB");
|
||||
v = q.pop_front(); `checks(v, "CC");
|
||||
end
|
||||
|
||||
// Unpacked methods also allowed. Not supported yet.
|
||||
// find()
|
||||
// find_index()
|
||||
// find_first()
|
||||
// find_first_index()
|
||||
// find_last()
|
||||
// find_last_index()
|
||||
// min()
|
||||
// max()
|
||||
// unique()
|
||||
// unique_index()
|
||||
// reverse()
|
||||
// sort()
|
||||
// rsort()
|
||||
// shuffle()
|
||||
// sum()
|
||||
// product()
|
||||
// and()
|
||||
// or()
|
||||
// xor()
|
||||
end
|
||||
endmodule
|
Loading…
Reference in New Issue
Block a user