Support queues (excluding {} notation and pattern assignments), bug545.

This commit is contained in:
Wilson Snyder 2019-12-01 12:35:49 -05:00
parent b81295230a
commit e28175108f
19 changed files with 574 additions and 10 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

83
test_regress/t/t_queue.v Normal file
View 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

View 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

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

View 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

View 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

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

View 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