Support queue of arrays

This commit is contained in:
Wilson Snyder 2020-12-12 19:19:16 -05:00
parent 0ca72f8098
commit 517fdb7587
9 changed files with 205 additions and 15 deletions

View File

@ -96,7 +96,7 @@ public:
// simply use a C style array (which is just a pointer). // simply use a C style array (which is just a pointer).
template <std::size_t T_Words> class VlWide final { template <std::size_t T_Words> class VlWide final {
WData m_storage[T_Words]; EData m_storage[T_Words];
public: public:
// cppcheck-suppress uninitVar // cppcheck-suppress uninitVar
@ -104,11 +104,17 @@ public:
~VlWide() = default; ~VlWide() = default;
VlWide(const VlWide&) = default; VlWide(const VlWide&) = default;
VlWide(VlWide&&) = default; VlWide(VlWide&&) = default;
// OPERATOR METHODS
VlWide& operator=(const VlWide&) = default; VlWide& operator=(const VlWide&) = default;
VlWide& operator=(VlWide&&) = default; VlWide& operator=(VlWide&&) = default;
const EData& operator[](size_t index) const { return m_storage[index]; };
EData& operator[](size_t index) { return m_storage[index]; };
operator WDataOutP() { return &m_storage[0]; }
// METHODS // METHODS
const WData& at(size_t index) const { return m_storage[index]; } const EData& at(size_t index) const { return m_storage[index]; }
WData& at(size_t index) { return m_storage[index]; } EData& at(size_t index) { return m_storage[index]; }
WData* data() { return &m_storage[0]; } WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; } const WData* data() const { return &m_storage[0]; }
bool operator<(const VlWide<T_Words>& rhs) const { bool operator<(const VlWide<T_Words>& rhs) const {
@ -788,6 +794,41 @@ void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
} }
} }
//===================================================================
// Verilog packed array container
// For when a standard C++[] array is not sufficient, e.g. an
// array under a queue, or methods operating on the array
template <class T_Value, std::size_t T_Depth> class VlUnpacked final {
private:
// TYPES
typedef std::array<T_Value, T_Depth> Array;
public:
typedef typename Array::const_iterator const_iterator;
private:
// MEMBERS
Array m_array; // State of the assoc array
public:
// CONSTRUCTORS
VlUnpacked() = default;
~VlUnpacked() = default;
VlUnpacked(const VlUnpacked&) = default;
VlUnpacked(VlUnpacked&&) = default;
VlUnpacked& operator=(const VlUnpacked&) = default;
VlUnpacked& operator=(VlUnpacked&&) = default;
// METHODS
// Raw access
WData* data() { return &m_array[0]; }
const WData* data() const { return &m_array[0]; }
T_Value& operator[](size_t index) { return m_array[index]; };
const T_Value& operator[](size_t index) const { return m_array[index]; };
};
//=================================================================== //===================================================================
// Verilog class reference container // Verilog class reference container
// There are no multithreaded locks on this; the base variable must // There are no multithreaded locks on this; the base variable must

View File

@ -2388,7 +2388,13 @@ public:
virtual void dump(std::ostream& str) const override; virtual void dump(std::ostream& str) const override;
virtual void dumpSmall(std::ostream& str) const; virtual void dumpSmall(std::ostream& str) const;
virtual bool hasDType() const override { return true; } virtual bool hasDType() const override { return true; }
virtual AstBasicDType* basicp() const = 0; // (Slow) recurse down to find basic data type /// Require VlUnpacked, instead of [] for POD elements.
/// A non-POD object is always compound, but some POD elements
/// are compound when methods calls operate on object, or when
/// under another compound-requiring object e.g. class
virtual bool isCompound() const = 0;
// (Slow) recurse down to find basic data type
virtual AstBasicDType* basicp() const = 0;
// recurses over typedefs/const/enum to next non-typeref type // recurses over typedefs/const/enum to next non-typeref type
virtual AstNodeDType* skipRefp() const = 0; virtual AstNodeDType* skipRefp() const = 0;
// recurses over typedefs to next non-typeref-or-const type // recurses over typedefs to next non-typeref-or-const type
@ -2480,6 +2486,7 @@ public:
ASTNODE_BASE_FUNCS(NodeUOrStructDType) ASTNODE_BASE_FUNCS(NodeUOrStructDType)
virtual const char* broken() const override; virtual const char* broken() const override;
virtual void dump(std::ostream& str) const override; virtual void dump(std::ostream& str) const override;
virtual bool isCompound() const { return false; } // Because don't support unpacked
// For basicp() we reuse the size to indicate a "fake" basic type of same size // For basicp() we reuse the size to indicate a "fake" basic type of same size
virtual AstBasicDType* basicp() const override { virtual AstBasicDType* basicp() const override {
return (isFourstate() return (isFourstate()

View File

@ -653,12 +653,16 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
} else if (const auto* adtypep = VN_CAST_CONST(dtypep, ClassRefDType)) { } else if (const auto* adtypep = VN_CAST_CONST(dtypep, ClassRefDType)) {
info.m_type = "VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(adtypep) + ">"; info.m_type = "VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(adtypep) + ">";
} else if (const auto* adtypep = VN_CAST_CONST(dtypep, UnpackArrayDType)) { } else if (const auto* adtypep = VN_CAST_CONST(dtypep, UnpackArrayDType)) {
if (compound) { if (adtypep->isCompound()) compound = true;
v3fatalSrc("Dynamic arrays or queues with unpacked elements are not yet supported");
}
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound); const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(compound);
info.m_type = sub.m_type; if (compound) {
info.m_dims = "[" + cvtToStr(adtypep->declRange().elements()) + "]" + sub.m_dims; info.m_type = "VlUnpacked<" + sub.m_type;
info.m_type += ", " + cvtToStr(adtypep->declRange().elements());
info.m_type += ">";
} else {
info.m_type = sub.m_type;
info.m_dims = "[" + cvtToStr(adtypep->declRange().elements()) + "]" + sub.m_dims;
}
} else if (const AstBasicDType* bdtypep = dtypep->basicp()) { } else if (const AstBasicDType* bdtypep = dtypep->basicp()) {
// We don't print msb()/lsb() as multidim packed would require recursion, // We don't print msb()/lsb() as multidim packed would require recursion,
// and may confuse users as C++ data is stored always with bit 0 used // and may confuse users as C++ data is stored always with bit 0 used
@ -1387,15 +1391,17 @@ void AstNodeDType::dumpSmall(std::ostream& str) const {
} }
void AstNodeArrayDType::dumpSmall(std::ostream& str) const { void AstNodeArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str); this->AstNodeDType::dumpSmall(str);
if (VN_IS(this, PackArrayDType)) { if (auto* adtypep = VN_CAST_CONST(this, UnpackArrayDType)) {
str << "p"; // uc = packed compound object, u = unpacked POD
str << (adtypep->isCompound() ? "uc" : "u");
} else { } else {
str << "u"; str << "p";
} }
str << declRange(); str << declRange();
} }
void AstNodeArrayDType::dump(std::ostream& str) const { void AstNodeArrayDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str); this->AstNodeDType::dump(str);
if (isCompound()) str << " [COMPOUND]";
str << " " << declRange(); str << " " << declRange();
} }
string AstPackArrayDType::prettyDTypeName() const { string AstPackArrayDType::prettyDTypeName() const {

View File

@ -407,6 +407,10 @@ public:
AstVarType varType() const { return m_varType; } // * = Type of variable AstVarType varType() const { return m_varType; } // * = Type of variable
bool isParam() const { return true; } bool isParam() const { return true; }
bool isGParam() const { return (varType() == AstVarType::GPARAM); } bool isGParam() const { return (varType() == AstVarType::GPARAM); }
virtual bool isCompound() const override {
v3fatalSrc("call isCompound on subdata type, not reference");
return false;
}
}; };
class AstTypedef final : public AstNode { class AstTypedef final : public AstNode {
@ -507,6 +511,7 @@ public:
virtual int widthTotalBytes() const override { return dtypep()->widthTotalBytes(); } virtual int widthTotalBytes() const override { return dtypep()->widthTotalBytes(); }
virtual string name() const override { return m_name; } virtual string name() const override { return m_name; }
virtual void name(const string& flag) override { m_name = flag; } virtual void name(const string& flag) override { m_name = flag; }
virtual bool isCompound() const override { return false; }
}; };
class AstAssocArrayDType final : public AstNodeDType { class AstAssocArrayDType final : public AstNodeDType {
@ -578,6 +583,7 @@ public:
virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); } virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); }
virtual bool isCompound() const override { return true; }
}; };
class AstBracketArrayDType final : public AstNodeDType { class AstBracketArrayDType final : public AstNodeDType {
@ -608,6 +614,7 @@ public:
virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
virtual int widthAlignBytes() const override { V3ERROR_NA_RETURN(0); } virtual int widthAlignBytes() const override { V3ERROR_NA_RETURN(0); }
virtual int widthTotalBytes() const override { V3ERROR_NA_RETURN(0); } virtual int widthTotalBytes() const override { V3ERROR_NA_RETURN(0); }
virtual bool isCompound() const override { return true; }
}; };
class AstDynArrayDType final : public AstNodeDType { class AstDynArrayDType final : public AstNodeDType {
@ -667,6 +674,7 @@ public:
virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); } virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); }
virtual bool isCompound() const override { return true; }
}; };
class AstPackArrayDType final : public AstNodeArrayDType { class AstPackArrayDType final : public AstNodeArrayDType {
@ -693,12 +701,14 @@ public:
} }
ASTNODE_NODE_FUNCS(PackArrayDType) ASTNODE_NODE_FUNCS(PackArrayDType)
virtual string prettyDTypeName() const override; virtual string prettyDTypeName() const override;
virtual bool isCompound() const override { return false; }
}; };
class AstUnpackArrayDType final : public AstNodeArrayDType { class AstUnpackArrayDType final : public AstNodeArrayDType {
// Array data type, ie "some_dtype var_name [2:0]" // Array data type, ie "some_dtype var_name [2:0]"
// Children: DTYPE (moved to refDTypep() in V3Width) // Children: DTYPE (moved to refDTypep() in V3Width)
// Children: RANGE (array bounds) // Children: RANGE (array bounds)
bool m_isCompound = false; // Non-POD subDType, or parent requires compound
public: public:
AstUnpackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep) AstUnpackArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp, AstRange* rangep)
: ASTGEN_SUPER(fl) { : ASTGEN_SUPER(fl) {
@ -721,8 +731,14 @@ public:
} }
ASTNODE_NODE_FUNCS(UnpackArrayDType) ASTNODE_NODE_FUNCS(UnpackArrayDType)
virtual string prettyDTypeName() const override; virtual string prettyDTypeName() const override;
virtual bool same(const AstNode* samep) const override {
const AstUnpackArrayDType* sp = static_cast<const AstUnpackArrayDType*>(samep);
return m_isCompound == sp->m_isCompound;
}
// Outer dimension comes first. The first element is this node. // Outer dimension comes first. The first element is this node.
std::vector<AstUnpackArrayDType*> unpackDimensions(); std::vector<AstUnpackArrayDType*> unpackDimensions();
void isCompound(bool flag) { m_isCompound = flag; }
virtual bool isCompound() const override { return m_isCompound; }
}; };
class AstUnsizedArrayDType final : public AstNodeDType { class AstUnsizedArrayDType final : public AstNodeDType {
@ -775,6 +791,7 @@ public:
virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); } virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); }
virtual bool isCompound() const override { return true; }
}; };
class AstBasicDType final : public AstNodeDType { class AstBasicDType final : public AstNodeDType {
@ -930,6 +947,7 @@ public:
rangep(nullptr); rangep(nullptr);
} }
} }
virtual bool isCompound() const override { return isString(); }
}; };
class AstConstDType final : public AstNodeDType { class AstConstDType final : public AstNodeDType {
@ -982,6 +1000,10 @@ public:
virtual AstNodeDType* skipRefToEnump() const override { return subDTypep()->skipRefToEnump(); } virtual AstNodeDType* skipRefToEnump() const override { return subDTypep()->skipRefToEnump(); }
virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); } virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); }
virtual bool isCompound() const override {
v3fatalSrc("call isCompound on subdata type, not reference");
return false;
}
}; };
class AstClassRefDType final : public AstNodeDType { class AstClassRefDType final : public AstNodeDType {
@ -1033,6 +1055,7 @@ public:
AstClass* classp() const { return m_classp; } AstClass* classp() const { return m_classp; }
void classp(AstClass* nodep) { m_classp = nodep; } void classp(AstClass* nodep) { m_classp = nodep; }
AstPin* paramsp() const { return VN_CAST(op4p(), Pin); } AstPin* paramsp() const { return VN_CAST(op4p(), Pin); }
virtual bool isCompound() const override { return true; }
}; };
class AstIfaceRefDType final : public AstNodeDType { class AstIfaceRefDType final : public AstNodeDType {
@ -1088,6 +1111,7 @@ public:
AstModport* modportp() const { return m_modportp; } AstModport* modportp() const { return m_modportp; }
void modportp(AstModport* modportp) { m_modportp = modportp; } void modportp(AstModport* modportp) { m_modportp = modportp; }
bool isModport() { return !m_modportName.empty(); } bool isModport() { return !m_modportName.empty(); }
virtual bool isCompound() const override { return true; } // But not relevant
}; };
class AstQueueDType final : public AstNodeDType { class AstQueueDType final : public AstNodeDType {
@ -1158,6 +1182,7 @@ public:
virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); } virtual int widthAlignBytes() const override { return subDTypep()->widthAlignBytes(); }
virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); } virtual int widthTotalBytes() const override { return subDTypep()->widthTotalBytes(); }
virtual bool isCompound() const override { return true; }
}; };
class AstRefDType final : public AstNodeDType { class AstRefDType final : public AstNodeDType {
@ -1259,6 +1284,10 @@ public:
AstNode* typeofp() const { return op2p(); } AstNode* typeofp() const { return op2p(); }
AstNode* classOrPackageOpp() const { return op3p(); } AstNode* classOrPackageOpp() const { return op3p(); }
AstPin* paramsp() const { return VN_CAST(op4p(), Pin); } AstPin* paramsp() const { return VN_CAST(op4p(), Pin); }
virtual bool isCompound() const override {
v3fatalSrc("call isCompound on subdata type, not reference");
return false;
}
}; };
class AstStructDType final : public AstNodeUOrStructDType { class AstStructDType final : public AstNodeUOrStructDType {
@ -1341,6 +1370,10 @@ public:
virtual string tag() const override { return m_tag; } virtual string tag() const override { return m_tag; }
int lsb() const { return m_lsb; } int lsb() const { return m_lsb; }
void lsb(int lsb) { m_lsb = lsb; } void lsb(int lsb) { m_lsb = lsb; }
virtual bool isCompound() const override {
v3fatalSrc("call isCompound on subdata type, not reference");
return false;
}
}; };
class AstVoidDType final : public AstNodeDType { class AstVoidDType final : public AstNodeDType {
@ -1368,6 +1401,7 @@ public:
virtual int widthAlignBytes() const override { return 1; } virtual int widthAlignBytes() const override { return 1; }
virtual int widthTotalBytes() const override { return 1; } virtual int widthTotalBytes() const override { return 1; }
virtual V3Hash sameHash() const override { return V3Hash(); } virtual V3Hash sameHash() const override { return V3Hash(); }
virtual bool isCompound() const override { return false; }
}; };
class AstEnumItem final : public AstNode { class AstEnumItem final : public AstNode {
@ -1489,6 +1523,7 @@ public:
for (AstNode* itemp = itemsp(); itemp; itemp = itemp->nextp()) count++; for (AstNode* itemp = itemsp(); itemp; itemp = itemp->nextp()) count++;
return count; return count;
} }
virtual bool isCompound() const override { return false; }
}; };
class AstParseTypeDType final : public AstNodeDType { class AstParseTypeDType final : public AstNodeDType {
@ -1510,6 +1545,10 @@ public:
virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; } virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
virtual int widthAlignBytes() const override { return 0; } virtual int widthAlignBytes() const override { return 0; }
virtual int widthTotalBytes() const override { return 0; } virtual int widthTotalBytes() const override { return 0; }
virtual bool isCompound() const override {
v3fatalSrc("call isCompound on subdata type, not reference");
return false;
}
}; };
//###################################################################### //######################################################################

View File

@ -549,7 +549,7 @@ private:
if (nodep->name() == "") nodep->name("genblk" + cvtToStr(m_genblkNum)); if (nodep->name() == "") nodep->name("genblk" + cvtToStr(m_genblkNum));
} }
if (nodep->generate() && nodep->name() == "" if (nodep->generate() && nodep->name() == ""
&& (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) { && (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) {
nodep->name("genblk" + cvtToStr(m_genblkAbove)); nodep->name("genblk" + cvtToStr(m_genblkAbove));
} }
if (nodep->name() != "") { if (nodep->name() != "") {

View File

@ -106,7 +106,8 @@ class SliceVisitor final : public AstNVisitor {
? snodep->declRange().elements() - 1 - offset ? snodep->declRange().elements() - 1 - offset
: offset)); : offset));
newp = new AstArraySel(nodep->fileline(), snodep->fromp()->cloneTree(false), leOffset); newp = new AstArraySel(nodep->fileline(), snodep->fromp()->cloneTree(false), leOffset);
} else if (VN_IS(nodep, ArraySel) || VN_IS(nodep, NodeVarRef) || VN_IS(nodep, NodeSel)) { } else if (VN_IS(nodep, ArraySel) || VN_IS(nodep, NodeVarRef) || VN_IS(nodep, NodeSel)
|| VN_IS(nodep, CMethodHard)) {
UINFO(9, " cloneSel(" << elements << "," << offset << ") " << nodep << endl); UINFO(9, " cloneSel(" << elements << "," << offset << ") " << nodep << endl);
int leOffset = !arrayp->rangep()->littleEndian() int leOffset = !arrayp->rangep()->littleEndian()
? arrayp->rangep()->elementsConst() - 1 - offset ? arrayp->rangep()->elementsConst() - 1 - offset

View File

@ -1451,9 +1451,10 @@ private:
// Cleanup array size // Cleanup array size
userIterateAndNext(nodep->rangep(), WidthVP(SELF, BOTH).p()); userIterateAndNext(nodep->rangep(), WidthVP(SELF, BOTH).p());
nodep->dtypep(nodep); // The array itself, not subDtype nodep->dtypep(nodep); // The array itself, not subDtype
if (VN_IS(nodep, UnpackArrayDType)) { if (auto* adtypep = VN_CAST(nodep, UnpackArrayDType)) {
// Historically array elements have width of the ref type not the full array // Historically array elements have width of the ref type not the full array
nodep->widthFromSub(nodep->subDTypep()); nodep->widthFromSub(nodep->subDTypep());
if (nodep->subDTypep()->skipRefp()->isCompound()) adtypep->isCompound(true);
} else { } else {
int width = nodep->subDTypep()->width() * nodep->rangep()->elementsConst(); int width = nodep->subDTypep()->width() * nodep->rangep()->elementsConst();
nodep->widthForce(width, width); nodep->widthForce(width, width);

21
test_regress/t/t_queue_array.pl Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env 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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
compile(
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,74 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2020 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`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;
typedef string sarray_t[2];
typedef sarray_t q_sarray_t[$];
typedef bit [95:0] wide_t;
typedef wide_t warray_t[2];
typedef warray_t q_warray_t[$];
initial begin
begin
q_sarray_t iq;
sarray_t a;
sarray_t b0;
sarray_t b1;
a[0] = "hello";
a[1] = "world";
iq.push_back(a);
a[0] = "bye";
a[1] = "world";
iq.push_back(a);
b0 = iq[0];
b1 = iq[1];
`checks(b0[0], "hello");
`checks(b0[1], "world");
`checks(b1[0], "bye");
`checks(b1[1], "world");
end
`ifndef verilator
// Need wide conversion into VlUnpacked types
// If we convert all arrays to VlUnpacked it works, so we need to track
// data types and insert conversions perhaps in V3Cast, but we currently
// don't know the output datatypes, so work needed.
begin
q_warray_t iq;
warray_t a;
warray_t b0;
a[0] = "abcdefg_ijkl";
a[1] = "012123123128";
iq.push_back(a);
b0 = iq[0];
`checks(b0[0], "abcdefg_ijkl");
`checks(b0[1], "012123123128");
end
`endif
$write("*-* All Finished *-*\n");
$finish;
end
endmodule