Add support for dynamic arrays, #379.

This commit is contained in:
Wilson Snyder 2020-03-07 10:24:27 -05:00
parent 8054fc47ea
commit e70cba77e6
16 changed files with 358 additions and 39 deletions

View File

@ -9,6 +9,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
** Add support for $dumpfile and $dumpvars, #2126. [Alexander Grobman]
** Add support for dynamic arrays, #379.
*** Add +verilator+noassert flag to disable assertion checking. [Tobias Wölfel]
*** Add check for assertOn for asserts, #2162. [Tobias Wölfel]

View File

@ -245,11 +245,12 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
}
//===================================================================
// Verilog queue container
// Verilog queue and dynamic array container
// There are no multithreaded locks on this; the base variable must
// be protected by other means
//
// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
// For dynamic arrays it is always zero
template <class T_Value, size_t T_MaxSize = 0> class VlQueue {
private:
// TYPES
@ -279,6 +280,21 @@ public:
void clear() { m_deque.clear(); }
void erase(size_t index) { if (VL_LIKELY(index < m_deque.size())) m_deque.erase(index); }
// Dynamic array new[] becomes a renew()
void renew(size_t size) {
clear();
m_deque.resize(size, atDefault());
}
// Dynamic array new[]() becomes a renew_copy()
void renew_copy(size_t size, const VlQueue<T_Value,T_MaxSize>& rhs) {
if (size == 0) {
clear();
} else {
*this = rhs;
m_deque.resize(size, atDefault());
}
}
// function void q.push_front(value)
void push_front(const T_Value& value) {
m_deque.push_front(value);
@ -304,6 +320,7 @@ public:
// 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;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index >= m_deque.size())) {
s_throwAway = atDefault();
return s_throwAway;
@ -313,6 +330,7 @@ public:
// Accessing. Verilog: v = assoc[index]
const T_Value& at(size_t index) const {
static T_Value s_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index >= m_deque.size())) return atDefault();
else return m_deque[index];
}

View File

@ -278,6 +278,17 @@ AstVar::VlArgTypeRecursed AstVar::vlArgTypeRecurse(bool forFunc, const AstNodeDT
VlArgTypeRecursed info;
info.m_oprefix = out;
return info;
} else if (const AstDynArrayDType* adtypep = VN_CAST_CONST(dtypep, DynArrayDType)) {
VlArgTypeRecursed sub = vlArgTypeRecurse(forFunc, adtypep->subDTypep(), true);
string out = "VlQueue<";
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 AstQueueDType* adtypep = VN_CAST_CONST(dtypep, QueueDType)) {
VlArgTypeRecursed sub = vlArgTypeRecurse(forFunc, adtypep->subDTypep(), true);
VlArgTypeRecursed info;
@ -1168,6 +1179,13 @@ void AstAssocArrayDType::dumpSmall(std::ostream& str) const {
string AstAssocArrayDType::prettyDTypeName() const {
return subDTypep()->prettyDTypeName() + "[" + keyDTypep()->prettyDTypeName() + "]";
}
void AstDynArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str<<"[]";
}
string AstDynArrayDType::prettyDTypeName() const {
return subDTypep()->prettyDTypeName() + "[]";
}
void AstQueueDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str<<"[queue]";

View File

@ -451,6 +451,56 @@ public:
virtual int widthTotalBytes() const { return subDTypep()->widthTotalBytes(); }
};
class AstDynArrayDType : public AstNodeDType {
// Dynamic array data type, ie "[]"
// Children: DTYPE (moved to refDTypep() in V3Width)
private:
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
public:
AstDynArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp)
: ASTGEN_SUPER(fl) {
childDTypep(dtp); // Only for parser
refDTypep(NULL);
dtypep(NULL); // V3Width will resolve
}
AstDynArrayDType(FileLine* fl, AstNodeDType* dtp)
: ASTGEN_SUPER(fl) {
refDTypep(dtp);
dtypep(NULL); // V3Width will resolve
}
ASTNODE_NODE_FUNCS(DynArrayDType)
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 AstAssocArrayDType* asamep = static_cast<const AstAssocArrayDType*>(samep);
return subDTypep() == asamep->subDTypep(); }
virtual bool similarDType(AstNodeDType* samep) const {
const AstAssocArrayDType* asamep = static_cast<const AstAssocArrayDType*>(samep);
return (subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp()));
}
virtual string prettyDTypeName() const;
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; } // (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)

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(), DynArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType)
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)

View File

@ -1503,6 +1503,9 @@ class EmitCImp : EmitCStmts {
return emitVarResetRecurse(varp, adtypep->subDTypep(), depth+1,
".atDefault()" + cvtarray);
}
else if (AstDynArrayDType* adtypep = VN_CAST(dtypep, DynArrayDType)) {
return emitVarResetRecurse(varp, adtypep->subDTypep(), depth+1, ".atDefault()");
}
else if (AstQueueDType* adtypep = VN_CAST(dtypep, QueueDType)) {
return emitVarResetRecurse(varp, adtypep->subDTypep(), depth+1, ".atDefault()");
}

View File

@ -50,6 +50,10 @@ class EmitCInlines : EmitCBaseVisitor {
v3Global.needHeavy(true);
iterateChildren(nodep);
}
virtual void visit(AstDynArrayDType* nodep) VL_OVERRIDE {
v3Global.needHeavy(true);
iterateChildren(nodep);
}
virtual void visit(AstQueueDType* nodep) VL_OVERRIDE {
v3Global.needHeavy(true);
iterateChildren(nodep);

View File

@ -457,7 +457,7 @@ private:
if (m_vup->prelim()) {
AstNodeDType* vdtypep = m_vup->dtypeNullp();
if (vdtypep && (VN_IS(vdtypep, AssocArrayDType)
|| VN_IS(vdtypep, AssocArrayDType)
|| VN_IS(vdtypep, DynArrayDType)
|| VN_IS(vdtypep, QueueDType))) {
nodep->v3error("Unsupported: Concatenation to form "
<< vdtypep->prettyDTypeNameQ() << "data type");
@ -536,7 +536,9 @@ private:
if (m_vup->prelim()) {
AstNodeDType* vdtypep = m_vup->dtypeNullp();
if (vdtypep
&& (VN_IS(vdtypep, AssocArrayDType) || VN_IS(vdtypep, QueueDType)
&& (VN_IS(vdtypep, AssocArrayDType)
|| VN_IS(vdtypep, DynArrayDType)
|| VN_IS(vdtypep, QueueDType)
|| VN_IS(vdtypep, UnpackArrayDType))) {
nodep->v3error("Unsupported: Replication to form " << vdtypep->prettyDTypeNameQ()
<< " data type");
@ -1219,6 +1221,14 @@ private:
nodep->dtypep(nodep); // The array itself, not subDtype
UINFO(4,"dtWidthed "<<nodep<<endl);
}
virtual void visit(AstDynArrayDType* nodep) VL_OVERRIDE {
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(AstQueueDType* nodep) VL_OVERRIDE {
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep));
@ -1445,9 +1455,13 @@ private:
if (nodep->childDTypep()) nodep->dtypep(moveChildDTypeEdit(nodep));
nodep->dtypep(iterateEditDTypep(nodep, nodep->dtypep()));
UASSERT_OBJ(nodep->dtypep(), nodep, "No dtype determined for var");
if (VN_IS(nodep->dtypeSkipRefp(), UnsizedArrayDType)) {
if (AstUnsizedArrayDType* unsizedp = VN_CAST(nodep->dtypeSkipRefp(), UnsizedArrayDType)) {
if (!(m_ftaskp && m_ftaskp->dpiImport())) {
nodep->v3error("Unsized/open arrays ('[]') are only supported in DPI imports");
UINFO(9, "Unsized becomes dynamic array " << nodep << endl);
AstDynArrayDType* newp
= new AstDynArrayDType(unsizedp->fileline(), unsizedp->subDTypep());
nodep->dtypep(newp);
v3Global.rootp()->typeTablep()->addTypesp(newp);
}
}
else if (nodep->isIO() && !(VN_IS(nodep->dtypeSkipRefp(), BasicDType)
@ -1778,6 +1792,7 @@ private:
if (memberSelStruct(nodep, adtypep)) return;
} else if (VN_IS(fromDtp, EnumDType)
|| VN_IS(fromDtp, AssocArrayDType)
|| VN_IS(fromDtp, DynArrayDType)
|| VN_IS(fromDtp, QueueDType)
|| VN_IS(fromDtp, BasicDType)) {
// Method call on enum without following parenthesis, e.g. "ENUM.next"
@ -1849,6 +1864,9 @@ private:
else if (AstAssocArrayDType* adtypep = VN_CAST(fromDtp, AssocArrayDType)) {
methodCallAssoc(nodep, adtypep);
}
else if (AstDynArrayDType* adtypep = VN_CAST(fromDtp, DynArrayDType)) {
methodCallDyn(nodep, adtypep);
}
else if (AstQueueDType* adtypep = VN_CAST(fromDtp, QueueDType)) {
methodCallQueue(nodep, adtypep);
}
@ -2059,6 +2077,41 @@ private:
if (lvalue) varrefp->lvalue(true);
}
}
void methodCallDyn(AstMethodCall* nodep, AstDynArrayDType* adtypep) {
AstCMethodHard* newp = NULL;
if (nodep->name() == "at") { // Created internally for []
methodOkArguments(nodep, 1, 1);
methodCallLValue(nodep, nodep->fromp(), true);
newp = new AstCMethodHard(nodep->fileline(),
nodep->fromp()->unlinkFrBack(),
"at", NULL);
newp->dtypeFrom(adtypep->subDTypep());
newp->protect(false);
newp->didWidth(true);
} else if (nodep->name() == "size") {
methodOkArguments(nodep, 0, 0);
newp = new AstCMethodHard(nodep->fileline(),
nodep->fromp()->unlinkFrBack(),
"size", NULL);
newp->dtypeSetSigned32();
newp->didWidth(true);
newp->protect(false);
} else if (nodep->name() == "delete") { // function void delete()
methodOkArguments(nodep, 0, 0);
methodCallLValue(nodep, nodep->fromp(), true);
newp = new AstCMethodHard(nodep->fileline(),
nodep->fromp()->unlinkFrBack(),
"clear", NULL);
newp->makeStatement();
} else {
nodep->v3error("Unsupported/unknown built-in dynamic array method "
<< nodep->prettyNameQ());
}
if (newp) {
newp->didWidth(true);
nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
void methodCallQueue(AstMethodCall* nodep, AstQueueDType* adtypep) {
AstCMethodHard* newp = NULL;
if (nodep->name() == "at") { // Created internally for []
@ -2305,7 +2358,26 @@ private:
}
virtual void visit(AstNewDynamic* nodep) VL_OVERRIDE {
if (nodep->didWidthAndSet()) return;
nodep->v3error("Unsupported: Dynamic array new");
AstDynArrayDType* adtypep = VN_CAST(m_vup->dtypeNullp(), DynArrayDType);
if (!adtypep) { // e.g. int a = new;
if (adtypep) UINFO(1, "Got adtypep " << adtypep << endl);
nodep->v3error("dynamic new() not expected in this context (data type must be dynamic array)");
return;
}
// The AstNodeAssign visitor will be soon be replacing this node, make sure it gets it
if (!VN_IS(nodep->backp(), NodeAssign)) {
if (adtypep) UINFO(1, "Got backp " << nodep->backp() << endl);
nodep->v3error("dynamic new() not expected in this context (expected under an assign)");
return;
}
nodep->dtypep(adtypep);
if (m_vup && m_vup->prelim()) {
iterateCheckSigned32(nodep, "new() size", nodep->sizep(), BOTH);
}
if (nodep->rhsp()) {
iterateCheckTyped(nodep, "Dynamic array new RHS", nodep->rhsp(), adtypep->subDTypep(),
BOTH);
}
}
virtual void visit(AstPattern* nodep) VL_OVERRIDE {
@ -2727,6 +2799,23 @@ private:
iterateCheckAssign(nodep, "Assign RHS", nodep->rhsp(), FINAL, lhsDTypep);
//if (debug()) nodep->dumpTree(cout, " AssignOut: ");
}
if (AstNewDynamic* dynp = VN_CAST(nodep->rhsp(), NewDynamic)) {
UINFO(9, "= new[] -> .resize(): " << nodep);
AstCMethodHard* newp;
if (!dynp->rhsp()) {
newp = new AstCMethodHard(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
"renew", dynp->sizep()->unlinkFrBack());
} else {
newp = new AstCMethodHard(nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
"renew_copy", dynp->sizep()->unlinkFrBack());
newp->addPinsp(dynp->rhsp()->unlinkFrBack());
}
newp->didWidth(true);
newp->protect(false);
newp->makeStatement();
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
}
virtual void visit(AstSFormatF* nodep) VL_OVERRIDE {
@ -2772,6 +2861,7 @@ private:
added = true;
newFormat += "%g";
} else if (VN_IS(dtypep, AssocArrayDType)
|| VN_IS(dtypep, DynArrayDType)
|| VN_IS(dtypep, QueueDType)) {
added = true;
newFormat += "%@";
@ -4225,8 +4315,8 @@ private:
AstNode* checkCvtUS(AstNode* nodep) {
if (nodep && nodep->isDouble()) {
nodep->v3error("Expected integral (non-real) input to "
<<nodep->backp()->prettyTypeName());
nodep->v3error("Expected integral (non-" << nodep->dtypep()->prettyDTypeName()
<< ") input to " << nodep->backp()->prettyTypeName());
nodep = spliceCvtS(nodep, true);
}
return nodep;

View File

@ -93,6 +93,8 @@ private:
}
else if (VN_IS(ddtypep, AssocArrayDType)) {
}
else if (VN_IS(ddtypep, DynArrayDType)) {
}
else if (VN_IS(ddtypep, QueueDType)) {
}
else if (const AstNodeUOrStructDType* adtypep = VN_CAST(ddtypep, NodeUOrStructDType)) {
@ -260,6 +262,15 @@ private:
if (debug()>=9) newp->dumpTree(cout, "--SELBTn: ");
nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
else if (AstDynArrayDType* adtypep = VN_CAST(ddtypep, DynArrayDType)) {
// SELBIT(array, index) -> CMETHODCALL(queue, "at", index)
AstNode* subp = rhsp;
AstCMethodHard* newp = new AstCMethodHard(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); VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
else if (AstQueueDType* adtypep = VN_CAST(ddtypep, QueueDType)) {
// SELBIT(array, index) -> CMETHODCALL(queue, "at", index)
AstNode* subp = rhsp;

View File

@ -1,9 +0,0 @@
%Error: t/t_dpi_openreg_bad.v:13: Unsized/open arrays ('[]') are only supported in DPI imports
: ... In instance t
reg a [];
^
%Error: t/t_dpi_openreg_bad.v:14: Unsized/open arrays ('[]') are only supported in DPI imports
: ... In instance t
input b [];
^
%Error: Exiting due to

View File

@ -1,20 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Copyright 2009 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.
module t (/*AUTOARG*/
// Inputs
b
);
reg a [];
input b [];
initial begin
$stop;
end
endmodule

20
test_regress/t/t_dynarray.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;

108
test_regress/t/t_dynarray.v Normal file
View File

@ -0,0 +1,108 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 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);
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=0;
integer i;
string v;
// verilator lint_off UNUSED
integer unused[];
// verilator lint_on UNUSED
typedef bit [7:0] byte_t;
byte_t a[];
byte_t b[];
always @ (posedge clk) begin
cyc <= cyc + 1;
begin
`checkh(a.size, 0);
v = $sformatf("%p", a); `checks(v, "'{} ");
a = new [3];
`checkh(a.size, 3);
a[0] = 10;
a[1] = 11;
a[2] = 12;
`checkh(a[0], 10);
`checkh(a[1], 11);
`checkh(a[2], 12);
v = $sformatf("%p", a); `checks(v, "'{'ha, 'hb, 'hc} ");
a.delete;
`checkh(a.size, 0);
a = new [2];
`ifdef verilator // Unsupported pattern assignment
a[0] = 15; a[1] = 16;
`else
a = '{15, 16};
`endif
`checkh(a.size, 2);
`checkh(a[0], 15);
`checkh(a[1], 16)
`ifdef verilator // Unsupported pattern assignment
a = new [1];
a[0] = 17;
`else
a = '{17};
`endif
`checkh(a.size, 1); // IEEE says resizes to smallest that fits pattern
`checkh(a[0], 17);
a = new[2];
a[0] = 5;
a[1] = 6;
`checkh(a[0], 5);
`checkh(a[1], 6);
a = new[2];
`checkh(a[0], 0);
`checkh(a[1], 0);
a[0] = 5;
a[1] = 6;
`checkh(a[0], 5);
`checkh(a[1], 6);
b = new [4](a);
`checkh(b.size, 4);
`checkh(b[0], 5);
`checkh(b[1], 6);
`checkh(b[2], 0);
`checkh(b[3], 0);
a = b;
`checkh(a.size, 4);
`checkh(a[0], 5);
`checkh(a[1], 6);
`checkh(a[2], 0);
`checkh(a[3], 0);
a = new [0];
`checkh(a.size, 0);
b = new [4](a);
`checkh(b.size, 4);
`checkh(b[0], 0);
`checkh(b[1], 0);
`checkh(b[2], 0);
`checkh(b[4], 0);
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,6 @@
%Warning-WIDTH: t/t_dynarray_bad.v:14: Operator NEWDYNAMIC expects 32 bits on the new() size, but new() size's VARREF 's' generates 64 bits.
: ... In instance t
a = new [s];
^~~
... Use "/* verilator lint_off WIDTH */" and lint_on around source to disable this message.
%Error: Internal Error: ../V3Number.cpp:#: Number operation called with non-logic (double or string) argument: '"str""

View File

@ -2,7 +2,7 @@
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
# 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.
@ -10,7 +10,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(linter => 1);
lint(
fails => $Self->{vlt_all},
fails => 1,
expect_filename => $Self->{golden_filename},
);

View File

@ -0,0 +1,17 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2020 by Wilson Snyder.
module t (/*AUTOARG*/);
integer a[];
string s;
initial begin
s = "str";
a = new [s]; // Bad
end
endmodule