Support $cast and new CASTCONST warning.

This commit is contained in:
Wilson Snyder 2020-12-05 22:58:36 -05:00
parent 8b8ebb0e43
commit 74ef35d3b3
33 changed files with 736 additions and 131 deletions

View File

@ -5,6 +5,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
* Verilator 4.107 devel
*** Support $cast and new CASTCONST warning.
*** Add --top option as alias of --top-module.
**** Fix passing parameter type instantiations by position number.

View File

@ -1717,7 +1717,7 @@ than using this option.
Disable all lint related warning messages, and all style warnings. This is
equivalent to "-Wno-ALWCOMBORDER -Wno-BSSPACE -Wno-CASEINCOMPLETE
-Wno-CASEOVERLAP -Wno-CASEX -Wno-CASEWITHX -Wno-CMPCONST -Wno-COLONPLUS
-Wno-CASEOVERLAP -Wno-CASEX -Wno-CASTCONST -Wno-CASEWITHX -Wno-CMPCONST -Wno-COLONPLUS
-Wno-ENDLABEL -Wno-IMPLICIT -Wno-LITENDIAN -Wno-PINCONNECTEMPTY
-Wno-PINMISSING -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED -Wno-UNUSED
-Wno-WIDTH" plus the list shown for Wno-style.
@ -1749,7 +1749,7 @@ Enables the specified warning message.
Enable all lint related warning messages (note by default they are already
enabled), but do not affect style messages. This is equivalent to
"-Wwarn-ALWCOMBORDER -Wwarn-BSSPACE -Wwarn-CASEINCOMPLETE
-Wwarn-CASEOVERLAP -Wwarn-CASEX -Wwarn-CASEWITHX -Wwarn-CMPCONST
-Wwarn-CASEOVERLAP -Wwarn-CASEX -Wwarn-CASTCONST -Wwarn-CASEWITHX -Wwarn-CMPCONST
-Wwarn-COLONPLUS -Wwarn-ENDLABEL -Wwarn-IMPLICIT -Wwarn-LITENDIAN
-Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-UNSIGNED -Wwarn-WIDTH".
@ -4317,15 +4317,6 @@ not overlap.
Ignoring this warning will only suppress the lint check, it will simulate
correctly.
=item CASEX
Warns that it is simply better style to use casez, and C<?> in place of
C<x>'s. See
L<http://www.sunburst-design.com/papers/CummingsSNUG1999Boston_FullParallelCase_rev1_1.pdf>
Ignoring this warning will only suppress the lint check, it will simulate
correctly.
=item CASEWITHX
Warns that a case statement contains a constant with a C<x>. Verilator is
@ -4336,6 +4327,25 @@ instead intended is to use a casez with C<?>.
Ignoring this warning will only suppress the lint check, it will simulate
correctly.
=item CASEX
Warns that it is simply better style to use casez, and C<?> in place of
C<x>'s. See
L<http://www.sunburst-design.com/papers/CummingsSNUG1999Boston_FullParallelCase_rev1_1.pdf>
Ignoring this warning will only suppress the lint check, it will simulate
correctly.
=item CASTCONST
Warns that a dynamic cast ($cast) is unnecessary as the $cast will always
succeed or fail. If it will always fail, the $cast is useless. If it will
always succeed a static cast may be preferred.
Ignoring this warning will only suppress the lint check, it will simulate
correctly. On other simulators, not fixing CASTCONST may result in
decreased performance.
=item CDCRSTLOGIC
With --cdc only, warns that asynchronous flop reset terms come from other

View File

@ -802,6 +802,17 @@ inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
return t;
}
template <typename T, typename U>
static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
VlClassRef<U> casted = std::dynamic_pointer_cast<U>(in);
if (VL_LIKELY(casted)) {
outr = casted;
return true;
} else {
return false;
}
}
//======================================================================
// Conversion functions

View File

@ -86,18 +86,19 @@ private:
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
return varrefp;
}
AstNode* newIfAssertOn(AstNode* nodep) {
AstNode* newIfAssertOn(AstNode* nodep, bool force) {
// Add a internal if to check assertions are on.
// Don't make this a AND term, as it's unlikely to need to test this.
FileLine* fl = nodep->fileline();
AstNode* newp
= new AstIf(fl,
// If assertions are off, have constant propagation rip them out later
// This allows syntax errors and such to be detected normally.
(v3Global.opt.assertOn()
? static_cast<AstNode*>(new AstCMath(fl, "Verilated::assertOn()", 1))
: static_cast<AstNode*>(new AstConst(fl, AstConst::BitFalse()))),
nodep, nullptr);
AstNode* newp = new AstIf(
fl,
(force ? new AstConst(fl, AstConst::BitTrue())
: // If assertions are off, have constant propagation rip them out later
// This allows syntax errors and such to be detected normally.
(v3Global.opt.assertOn()
? static_cast<AstNode*>(new AstCMath(fl, "Verilated::assertOn()", 1))
: static_cast<AstNode*>(new AstConst(fl, AstConst::BitFalse())))),
nodep, nullptr);
newp->user1(true); // Don't assert/cover this if
return newp;
}
@ -114,7 +115,7 @@ private:
AstNode* newFireAssert(AstNode* nodep, const string& message) {
AstNode* bodysp = newFireAssertUnchecked(nodep, message);
bodysp = newIfAssertOn(bodysp);
bodysp = newIfAssertOn(bodysp, false);
return bodysp;
}
@ -154,20 +155,21 @@ private:
if (bodysp && passsp) bodysp = bodysp->addNext(passsp);
ifp = new AstIf(nodep->fileline(), propp, bodysp, nullptr);
bodysp = ifp;
} else if (VN_IS(nodep, Assert)) {
} else if (VN_IS(nodep, Assert) || VN_IS(nodep, AssertIntrinsic)) {
if (nodep->immediate()) {
++m_statAsImm;
} else {
++m_statAsNotImm;
}
if (passsp) passsp = newIfAssertOn(passsp);
if (failsp) failsp = newIfAssertOn(failsp);
bool force = VN_IS(nodep, AssertIntrinsic);
if (passsp) passsp = newIfAssertOn(passsp, force);
if (failsp) failsp = newIfAssertOn(failsp, force);
if (!failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
ifp = new AstIf(nodep->fileline(), propp, passsp, failsp);
// It's more LIKELY that we'll take the nullptr if clause
// than the sim-killing else clause:
ifp->branchPred(VBranchPred::BP_LIKELY);
bodysp = newIfAssertOn(ifp);
bodysp = newIfAssertOn(ifp, force);
} else {
nodep->v3fatalSrc("Unknown node type");
}
@ -417,6 +419,10 @@ private:
iterateChildren(nodep);
newPslAssertion(nodep, nodep->failsp());
}
virtual void visit(AstAssertIntrinsic* nodep) override {
iterateChildren(nodep);
newPslAssertion(nodep, nodep->failsp());
}
virtual void visit(AstCover* nodep) override {
iterateChildren(nodep);
newPslAssertion(nodep, nullptr);

View File

@ -380,6 +380,7 @@ public:
ENUM_NEXT, // V3Width processes
ENUM_PREV, // V3Width processes
ENUM_NAME, // V3Width processes
ENUM_VALID, // V3Width processes
//
MEMBER_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes
//
@ -408,7 +409,7 @@ public:
"DIM_LOW", "DIM_RIGHT", "DIM_SIZE", "DIM_UNPK_DIMENSIONS",
"DT_PUBLIC",
"ENUM_BASE", "ENUM_FIRST", "ENUM_LAST", "ENUM_NUM",
"ENUM_NEXT", "ENUM_PREV", "ENUM_NAME",
"ENUM_NEXT", "ENUM_PREV", "ENUM_NAME", "ENUM_VALID",
"MEMBER_BASE",
"TYPENAME",
"VAR_BASE", "VAR_CLOCK_ENABLE", "VAR_PUBLIC",

View File

@ -3460,6 +3460,30 @@ public:
virtual bool brokeLhsMustBeLvalue() const override { return true; }
};
class AstExprStmt final : public AstNodeMath {
// Perform a statement, often assignment inside an expression/math node,
// the parent gets passed the 'resultp()'.
// resultp is evaluated AFTER the statement(s).
public:
AstExprStmt(FileLine* fl, AstNode* stmtsp, AstNode* resultp)
: ASTGEN_SUPER(fl) {
addOp1p(stmtsp);
setOp2p(resultp); // Possibly in future nullptr could mean return rhsp()
dtypeFrom(resultp);
}
ASTNODE_NODE_FUNCS(ExprStmt)
// ACCESSORS
AstNode* stmtsp() const { return op1p(); }
void addStmtsp(AstNode* nodep) { addOp1p(nodep); }
AstNode* resultp() const { return op2p(); }
// METHODS
virtual string emitVerilog() override { V3ERROR_NA_RETURN(""); }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual bool cleanOut() const override { return false; }
virtual V3Hash sameHash() const override { return V3Hash(); }
virtual bool same(const AstNode*) const override { return true; }
};
class AstComment final : public AstNodeStmt {
// Some comment to put into the output stream
// Parents: {statement list}
@ -6050,9 +6074,8 @@ public:
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override {
return new AstCastDynamic(this->fileline(), lhsp, rhsp);
}
virtual string emitVerilog() override { return "%f$cast(%l, %r)"; }
// Non-existent filehandle returns EOF
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual string emitVerilog() override { return "%f$cast(%r, %l)"; }
virtual string emitC() override { return "VL_DYNAMIC_CAST(%r, %l)"; }
virtual bool cleanOut() const override { return true; }
virtual bool cleanLhs() const override { return true; }
virtual bool cleanRhs() const override { return true; }
@ -8582,6 +8605,18 @@ public:
AstNode* failsp() const { return op3p(); } // op3 = if assertion fails
};
class AstAssertIntrinsic final : public AstNodeCoverOrAssert {
// A $cast or other compiler inserted assert, that must run even without --assert option
public:
ASTNODE_NODE_FUNCS(AssertIntrinsic)
AstAssertIntrinsic(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp,
bool immediate, const string& name = "")
: ASTGEN_SUPER(fl, propp, passsp, immediate, name) {
addNOp3p(failsp);
}
AstNode* failsp() const { return op3p(); } // op3 = if assertion fails
};
class AstCover final : public AstNodeCoverOrAssert {
public:
ASTNODE_NODE_FUNCS(Cover)

View File

@ -818,6 +818,15 @@ public:
}
puts("}\n");
}
virtual void visit(AstExprStmt* nodep) override {
// GCC allows compound statements in expressions, but this is not standard.
// So we use an immediate-evaluation lambda and comma operator
putbs("([&]() {\n");
iterateAndNextNull(nodep->stmtsp());
puts("}(), ");
iterateAndNextNull(nodep->resultp());
puts(")");
}
virtual void visit(AstStop* nodep) override {
puts("VL_STOP_MT(");
putsQuoted(protect(nodep->fileline()->filename()));
@ -1061,6 +1070,13 @@ public:
emitOpName(nodep, "VL_STREAML_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)", nodep->lhsp(),
nodep->rhsp(), nullptr);
}
virtual void visit(AstCastDynamic* nodep) override {
putbs("VL_CAST_DYNAMIC(");
iterateAndNextNull(nodep->lhsp());
puts(", ");
iterateAndNextNull(nodep->rhsp());
puts(")");
}
virtual void visit(AstCountBits* nodep) override {
putbs("VL_COUNTBITS_");
emitIQW(nodep->lhsp());

View File

@ -71,6 +71,7 @@ public:
CASEOVERLAP, // Case statements overlap
CASEWITHX, // Case with X values
CASEX, // Casex
CASTCONST, // Cast is constant
CDCRSTLOGIC, // Logic in async reset path
CLKDATA, // Clock used as data
CMPCONST, // Comparison is constant due to limited range
@ -153,7 +154,7 @@ public:
" EC_FIRST_WARN",
"ALWCOMBORDER", "ASSIGNDLY", "ASSIGNIN",
"BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE",
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CDCRSTLOGIC", "CLKDATA",
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA",
"CMPCONST", "COLONPLUS", "COMBDLY", "CONTASSREG",
"DEFPARAM", "DECLFILENAME", "DEPRECATED",
"ENDLABEL", "GENCLK", "HIERBLOCK",
@ -195,9 +196,10 @@ public:
// Warnings that are lint only
bool lintError() const {
return (m_e == ALWCOMBORDER || m_e == BSSPACE || m_e == CASEINCOMPLETE
|| m_e == CASEOVERLAP || m_e == CASEWITHX || m_e == CASEX || m_e == CMPCONST
|| m_e == COLONPLUS || m_e == ENDLABEL || m_e == IMPLICIT || m_e == LITENDIAN
|| m_e == PINMISSING || m_e == REALCVT || m_e == UNSIGNED || m_e == WIDTH);
|| m_e == CASEOVERLAP || m_e == CASEWITHX || m_e == CASEX || m_e == CASTCONST
|| m_e == CMPCONST || m_e == COLONPLUS || m_e == ENDLABEL || m_e == IMPLICIT
|| m_e == LITENDIAN || m_e == PINMISSING || m_e == REALCVT || m_e == UNSIGNED
|| m_e == WIDTH);
}
// Warnings that are style only
bool styleError() const {

View File

@ -100,6 +100,12 @@ std::ostream& operator<<(std::ostream& str, const Determ& rhs) {
return str << s_det[rhs];
}
enum Castable : uint8_t { UNSUPPORTED, COMPATIBLE, DYNAMIC_ENUM, DYNAMIC_CLASS, INCOMPATIBLE };
std::ostream& operator<<(std::ostream& str, const Castable& rhs) {
static const char* const s_det[] = {"UNSUP", "COMPAT", "DYN_ENUM", "DYN_CLS", "INCOMPAT"};
return str << s_det[rhs];
}
//######################################################################
// Width state, as a visitor of each AstNode
@ -1626,11 +1632,70 @@ private:
}
virtual void visit(AstCastDynamic* nodep) override {
nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED); // Spec says integer return
nodep->v3warn(E_UNSUPPORTED, "Unsupported: $cast. Suggest try static cast.");
AstNode* newp = new AstConst(nodep->fileline(), 1);
userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
AstNodeDType* toDtp = nodep->top()->dtypep()->skipRefToEnump();
AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
FileLine* fl = nodep->fileline();
const auto castable = computeCastable(toDtp, fromDtp, nodep->fromp());
AstNode* newp;
if (castable == DYNAMIC_CLASS) {
// Keep in place, will compute at runtime
return;
} else if (castable == DYNAMIC_ENUM) {
// TODO is from is a constant we could simplify, though normal constant
// elimination should do much the same
// Form: "( ((v > size) ? false : enum_valid[v[N:0]])
// ? ExprStmt(ExprAssign(out, Cast(v, type)), 1) : 0)"
auto* enumDtp = VN_CAST(toDtp, EnumDType);
UASSERT_OBJ(enumDtp, nodep, "$cast determined as enum, but not enum type");
uint64_t maxval = enumMaxValue(nodep, enumDtp);
int selwidth = V3Number::log2b(maxval) + 1; // Width to address a bit
AstVar* varp = enumVarp(enumDtp, AstAttrType::ENUM_VALID, (1ULL << selwidth) - 1);
AstVarRef* varrefp = new AstVarRef(fl, varp, VAccess::READ);
varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
FileLine* fl_nowarn = new FileLine(fl);
fl_nowarn->warnOff(V3ErrorCode::WIDTH, true);
auto* testp = new AstCond{
fl,
new AstGt{fl_nowarn, nodep->fromp()->cloneTree(false),
new AstConst{fl_nowarn, AstConst::Unsized64{}, maxval}},
new AstConst{fl, AstConst::BitFalse{}},
new AstArraySel{fl, varrefp,
new AstSel{fl, nodep->fromp()->cloneTree(false), 0, selwidth}}};
newp = new AstCond{fl, testp,
new AstExprStmt{fl,
new AstAssign{fl, nodep->top()->unlinkFrBack(),
nodep->fromp()->unlinkFrBack()},
new AstConst{fl, AstConst::Signed32(), 1}},
new AstConst{fl, AstConst::Signed32(), 0}};
} else if (castable == COMPATIBLE) {
nodep->v3warn(CASTCONST, "$cast will always return one as "
<< toDtp->prettyDTypeNameQ()
<< " is always castable from "
<< fromDtp->prettyDTypeNameQ() << '\n'
<< nodep->warnMore() << "... Suggest static cast");
newp = new AstExprStmt{
fl,
new AstAssign{fl, nodep->top()->unlinkFrBack(),
new AstCast{fl, nodep->fromp()->unlinkFrBack(), toDtp}},
new AstConst{fl, AstConst::Signed32(), 1}};
} else if (castable == INCOMPATIBLE) {
newp = new AstConst{fl, 0};
nodep->v3warn(CASTCONST, "$cast will always return zero as "
<< toDtp->prettyDTypeNameQ() << " is not castable from "
<< fromDtp->prettyDTypeNameQ());
} else {
newp = new AstConst{fl, 0};
nodep->v3warn(E_UNSUPPORTED, "Unsupported: $cast to "
<< toDtp->prettyDTypeNameQ() << " from "
<< fromDtp->prettyDTypeNameQ() << '\n'
<< nodep->warnMore()
<< "... Suggest try static cast");
}
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
userIterate(newp, m_vup);
}
virtual void visit(AstCastParse* nodep) override {
// nodep->dtp could be data type, or a primary_constant
@ -1651,53 +1716,86 @@ private:
}
virtual void visit(AstCast* nodep) override {
nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
// if (debug()) nodep->dumpTree(cout, " CastPre: ");
userIterateAndNext(nodep->fromp(), WidthVP(SELF, PRELIM).p());
// When more general casts are supported, the cast elimination will be done later.
// For now, replace it ASAP, so widthing can propagate easily
// The cast may change signing, but we don't know the sign yet. Make it so.
// Note we don't sign fromp() that would make the algorithm O(n^2) if lots of casting.
AstBasicDType* basicp = nodep->dtypep()->basicp();
UASSERT_OBJ(basicp, nodep, "Unimplemented: Casting non-simple data type");
if (m_vup->prelim()) {
// if (debug()) nodep->dumpTree(cout, " CastPre: ");
userIterateAndNext(nodep->fromp(), WidthVP(SELF, PRELIM).p());
// When implement more complicated types need to convert childDTypep to
// dtypep() not as a child
if (!basicp->isDouble() && !nodep->fromp()->isDouble()) {
// Note castSized might modify nodep->fromp()
int width = nodep->dtypep()->width();
castSized(nodep, nodep->fromp(), width);
AstNodeDType* toDtp = nodep->dtypep()->skipRefToEnump();
AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
const auto castable = computeCastable(toDtp, fromDtp, nodep->fromp());
bool bad = false;
if (castable == UNSUPPORTED) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: static cast to "
<< toDtp->prettyDTypeNameQ() << " from "
<< fromDtp->prettyDTypeNameQ());
bad = true;
} else if (castable == COMPATIBLE || castable == DYNAMIC_ENUM) {
; // Continue
} else if (castable == DYNAMIC_CLASS) {
nodep->v3error("Dynamic, not static cast, required to cast "
<< toDtp->prettyDTypeNameQ() << " from "
<< fromDtp->prettyDTypeNameQ() << '\n'
<< nodep->warnMore() << "... Suggest dynamic $cast");
bad = true;
} else if (castable == INCOMPATIBLE) {
nodep->v3error("Incompatible types to static cast to "
<< toDtp->prettyDTypeNameQ() << " from "
<< fromDtp->prettyDTypeNameQ() << '\n');
bad = true;
} else {
iterateCheck(nodep, "value", nodep->fromp(), SELF, FINAL, nodep->fromp()->dtypep(),
EXTEND_EXP, false);
nodep->v3fatalSrc("bad casting case");
}
AstNode* newp = nodep->fromp()->unlinkFrBack();
if (basicp->isDouble() && !newp->isDouble()) {
if (newp->isSigned()) {
newp = new AstISToRD(nodep->fileline(), newp);
// For now, replace it ASAP, so widthing can propagate easily
// The cast may change signing, but we don't know the sign yet. Make it so.
// Note we don't sign fromp() that would make the algorithm O(n^2) if lots of casting.
AstNode* newp = nullptr;
if (bad) {
} else if (AstBasicDType* basicp = toDtp->basicp()) {
if (!basicp->isDouble() && !fromDtp->isDouble()) {
int width = toDtp->width();
castSized(nodep, nodep->fromp(), width);
// Note castSized might modify nodep->fromp()
} else {
newp = new AstIToRD(nodep->fileline(), newp);
iterateCheck(nodep, "value", nodep->lhsp(), SELF, FINAL, fromDtp, EXTEND_EXP,
false);
}
} else if (!basicp->isDouble() && newp->isDouble()) {
if (basicp->isSigned()) {
newp = new AstRToIRoundS(nodep->fileline(), newp);
if (basicp->isDouble() && !nodep->fromp()->isDouble()) {
if (nodep->fromp()->isSigned()) {
newp = new AstISToRD(nodep->fileline(), nodep->fromp()->unlinkFrBack());
} else {
newp = new AstIToRD(nodep->fileline(), nodep->fromp()->unlinkFrBack());
}
} else if (!basicp->isDouble() && nodep->fromp()->isDouble()) {
if (basicp->isSigned()) {
newp
= new AstRToIRoundS(nodep->fileline(), nodep->fromp()->unlinkFrBack());
} else {
newp = new AstUnsigned(
nodep->fileline(),
new AstRToIS(nodep->fileline(), nodep->fromp()->unlinkFrBack()));
}
} else if (basicp->isSigned() && !nodep->fromp()->isSigned()) {
newp = new AstSigned(nodep->fileline(), nodep->fromp()->unlinkFrBack());
} else if (!basicp->isSigned() && nodep->fromp()->isSigned()) {
newp = new AstUnsigned(nodep->fileline(), nodep->fromp()->unlinkFrBack());
} else {
newp = new AstUnsigned(nodep->fileline(),
new AstRToIS(nodep->fileline(), newp));
// Can just remove cast
}
} else if (basicp->isSigned() && !newp->isSigned()) {
newp = new AstSigned(nodep->fileline(), newp);
} else if (!basicp->isSigned() && newp->isSigned()) {
newp = new AstUnsigned(nodep->fileline(), newp);
} else if (VN_IS(toDtp, ClassRefDType)) {
// Can just remove cast
} else {
// newp = newp; // Can just remove cast
nodep->v3fatalSrc("Unimplemented: Casting non-simple data type "
<< toDtp->prettyDTypeNameQ());
}
if (!newp) newp = nodep->fromp()->unlinkFrBack();
nodep->lhsp(newp);
// if (debug()) nodep->dumpTree(cout, " CastOut: ");
if (debug()) nodep->dumpTree(cout, " CastOut: ");
if (debug()) nodep->backp()->dumpTree(cout, " CastOutUpUp: ");
}
if (m_vup->final()) {
iterateCheck(nodep, "value", nodep->lhsp(), SELF, FINAL, nodep->lhsp()->dtypep(),
EXTEND_EXP, false);
AstNode* underp = nodep->lhsp()->unlinkFrBack();
if (debug()) underp->dumpTree(cout, " CastRep: ");
nodep->replaceWith(underp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
@ -3603,6 +3701,12 @@ private:
iterateCheckBool(nodep, "If", nodep->condp(), BOTH); // it's like an if() condition.
// if (debug()) nodep->dumpTree(cout, " IfOut: ");
}
virtual void visit(AstExprStmt* nodep) override {
userIterateAndNext(nodep->stmtsp(), nullptr);
// expected result is same as parent's expected result
userIterateAndNext(nodep->resultp(), m_vup);
nodep->dtypeFrom(nodep->resultp());
}
virtual void visit(AstNodeAssign* nodep) override {
// IEEE-2012 10.7, 11.8.2, 11.8.3, 11.5: (Careful of 11.8.1 which is
@ -4001,6 +4105,12 @@ private:
userIterateAndNext(nodep->passsp(), nullptr);
userIterateAndNext(nodep->failsp(), nullptr);
}
virtual void visit(AstAssertIntrinsic* nodep) override {
assertAtStatement(nodep);
iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition.
userIterateAndNext(nodep->passsp(), nullptr);
userIterateAndNext(nodep->failsp(), nullptr);
}
virtual void visit(AstCover* nodep) override {
assertAtStatement(nodep);
iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition.
@ -5730,6 +5840,12 @@ private:
AstNodeDType* basep;
if (attrType == AstAttrType::ENUM_NAME) {
basep = nodep->findStringDType();
} else if (attrType == AstAttrType::ENUM_VALID) {
// TODO in theory we could bit-pack the bits in the table, but
// would require additional operations to extract, so only
// would be worth it for larger tables which perhaps could be
// better handled with equation generation?
basep = nodep->findBitDType();
} else {
basep = nodep->dtypep();
}
@ -5752,6 +5868,8 @@ private:
initp->defaultp(new AstConst(nodep->fileline(), AstConst::String(), ""));
} else if (attrType == AstAttrType::ENUM_NEXT || attrType == AstAttrType::ENUM_PREV) {
initp->defaultp(new AstConst(nodep->fileline(), V3Number(nodep, nodep->width(), 0)));
} else if (attrType == AstAttrType::ENUM_VALID) {
initp->defaultp(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
} else {
nodep->v3fatalSrc("Bad case");
}
@ -5776,6 +5894,8 @@ private:
values[i] = (nextp ? nextp : firstp)->valuep()->cloneTree(false); // A const
} else if (attrType == AstAttrType::ENUM_PREV) {
values[i] = prevp->valuep()->cloneTree(false); // A const
} else if (attrType == AstAttrType::ENUM_VALID) {
values[i] = new AstConst(nodep->fileline(), AstConst::BitTrue{});
} else {
nodep->v3fatalSrc("Bad case");
}
@ -5785,8 +5905,7 @@ private:
}
// Add all specified values to table
for (unsigned i = 0; i < (msbdim + 1); ++i) {
AstNode* valp = values[i];
if (valp) initp->addIndexValuep(i, valp);
if (values[i]) initp->addIndexValuep(i, values[i]);
}
userIterate(varp, nullptr); // May have already done $unit so must do this var
m_tableMap.insert(make_pair(make_pair(nodep, attrType), varp));
@ -5860,6 +5979,48 @@ private:
return false;
}
//----------------------------------------------------------------------
// METHODS - casting
static Castable computeCastable(AstNodeDType* toDtp, AstNodeDType* fromDtp,
AstNode* fromConstp) {
const auto castable = computeCastableImp(toDtp, fromDtp, fromConstp);
UINFO(9, " castable=" << castable << " for " << toDtp << endl);
UINFO(9, " =?= " << fromDtp << endl);
UINFO(9, " const= " << fromConstp << endl);
return castable;
}
static Castable computeCastableImp(AstNodeDType* toDtp, AstNodeDType* fromDtp,
AstNode* fromConstp) {
Castable castable = UNSUPPORTED;
toDtp = toDtp->skipRefToEnump();
fromDtp = fromDtp->skipRefToEnump();
if (toDtp == fromDtp) return COMPATIBLE;
// UNSUP unpacked struct/unions (treated like BasicDType)
if (VN_IS(toDtp, BasicDType) || VN_IS(toDtp, NodeUOrStructDType)) {
if (VN_IS(fromDtp, BasicDType)) return COMPATIBLE;
if (VN_IS(fromDtp, EnumDType)) return COMPATIBLE;
if (VN_IS(fromDtp, NodeUOrStructDType)) return COMPATIBLE;
} else if (VN_IS(toDtp, EnumDType)) {
if (VN_IS(fromDtp, BasicDType) || VN_IS(fromDtp, EnumDType)) return DYNAMIC_ENUM;
} else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromConstp, Const)) {
if (VN_IS(fromConstp, Const) && VN_CAST(fromConstp, Const)->num().isNull())
return COMPATIBLE;
} else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromDtp, ClassRefDType)) {
const auto toClassp = VN_CAST(toDtp, ClassRefDType)->classp();
const auto fromClassp = VN_CAST(fromDtp, ClassRefDType)->classp();
bool downcast = AstClass::isClassExtendedFrom(toClassp, fromClassp);
bool upcast = AstClass::isClassExtendedFrom(fromClassp, toClassp);
if (upcast) {
return COMPATIBLE;
} else if (downcast) {
return DYNAMIC_CLASS;
} else {
return INCOMPATIBLE;
}
}
return castable;
}
//----------------------------------------------------------------------
// METHODS - special type detection
void assertAtStatement(AstNode* nodep) {

View File

@ -3667,7 +3667,9 @@ system_t_call<nodep>: // IEEE: system_tf_call (as task)
| yD_WRITEMEMH '(' expr ',' idClassSel ',' expr ',' expr ')' { $$ = new AstWriteMem($1, true, $3, $5, $7, $9); }
//
| yD_CAST '(' expr ',' expr ')'
{ $$ = new AstAssert($1, new AstCastDynamic($1, $5, $3), nullptr, nullptr, true); }
{ FileLine* fl_nowarn = new FileLine($1);
fl_nowarn->warnOff(V3ErrorCode::WIDTH, true);
$$ = new AstAssertIntrinsic(fl_nowarn, new AstCastDynamic(fl_nowarn, $5, $3), nullptr, nullptr, true); }
//
// Any system function as a task
| system_f_call_or_t { $$ = new AstSysFuncAsTask($<fl>1, $1); }

View File

@ -768,7 +768,7 @@ sub _exit {
if ($self->ok) {
$self->oprint("Self PASSED\n");
} elsif ($self->skips && !$self->errors) {
$self->oprint("%Skip: $self->{skips}\n");
$self->oprint("-Skip: $self->{skips}\n");
} elsif ($self->unsupporteds && !$self->errors) {
$self->oprint("%Unsupported: $self->{unsupporteds}\n");
} else {

21
test_regress/t/t_cast_class.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 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.
# 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,39 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2011 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
class Base;
int b;
endclass
class BaseExtended extends Base;
int e;
endclass
module t;
Base v_cls_a;
BaseExtended v_cls_ab;
BaseExtended v_cls_ab1;
initial begin
v_cls_a = Base'(null);
if (v_cls_a != null) $stop;
v_cls_ab = new;
v_cls_ab.b = 10;
v_cls_ab.e = 20;
v_cls_ab1 = BaseExtended'(v_cls_ab);
if (v_cls_ab1.b != 10) $stop;
if (v_cls_ab1.e != 20) $stop;
v_cls_a = Base'(v_cls_ab);
if (v_cls_a.b != 10) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,10 @@
%Error: t/t_cast_class_incompat_bad.v:26:16: Dynamic, not static cast, required to cast 'CLASSREFDTYPE 'BaseExtended'' from 'CLASSREFDTYPE 'Base''
: ... In instance t
: ... Suggest dynamic $cast
26 | cls_ab = BaseExtended'(cls_a);
| ^~~~~~~~~~~~
%Error: t/t_cast_class_incompat_bad.v:27:15: Incompatible types to static cast to 'CLASSREFDTYPE 'Other'' from 'CLASSREFDTYPE 'BaseExtended''
: ... In instance t
27 | other = Other'(cls_ab);
| ^~~~~
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2010 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(linter => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,30 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2011 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
class Base;
endclass
class BaseExtended extends Base;
endclass
class Other;
endclass
typedef Base Base_t;
typedef BaseExtended BaseExtended_t;
typedef Other Other_t;
module t;
Base_t cls_a;
BaseExtended_t cls_ab;
Other_t other;
initial begin
cls_a = new;
cls_ab = BaseExtended'(cls_a); // bad-need dyn
other = Other'(cls_ab); // bad-incompat
end
endmodule

21
test_regress/t/t_cast_types.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 2020 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,136 @@
// 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 TRY_ASSIGN(a,b) a = b
`define TRY_CAST(a,b) a = type(a)'(b)
`ifdef VERILATOR
`define TRY_DYNAMIC(a,b) // UNSUP $cast
`define TRY_BAD(a,b) // UNSUP $cast
`else
`define TRY_DYNAMIC(a,b) if (1 != $cast(a, b)) $stop
`define TRY_BAD(a,b) if (0 != $cast(a, b)) $stop
`endif
`define MATCHING(a,b) `TRY_ASSIGN(a,b)
`define EQUIVALENT(a,b) `TRY_ASSIGN(a,b)
`define COMPATIBLE(a,b) `TRY_ASSIGN(a,b)
`define CAST_COMPATIBLE(a,b) `TRY_CAST(a,b)
`define CAST_COMPATIBLE_ENUM(a,b) `TRY_CAST(a,b)
`define CAST_COMPATIBLE_DYNAMIC(a,b) `TRY_DYNAMIC(a,b)
`define INCOMPATIBLE(a,b) `TRY_BAD(a,b)
`define STRING_LITERAL "literal" // IEEE 5.9 - to packed or unpacked per IEEE 6.24
class Base;
endclass
class BaseExtended extends Base;
endclass
class Other;
endclass
typedef enum { A_ZERO, A_ONE } Enum_A_t;
typedef enum { B_ZERO, B_ONE } Enum_B_t;
typedef int int_t;
typedef struct packed { int a; int b; } stpack_t;
typedef bit signed [7:0] simple_a_t;
typedef bit signed [7:0] simple_a1_t;
module t (/*AUTOARG*/);
real v_real; // IEEE 6.12.2 - by rounding
string v_string;
int v_int;
int_t v_int_t;
chandle v_chandle;
Enum_A_t v_enum_a;
Enum_A_t v_enum_a1;
Enum_B_t v_enum_b;
stpack_t v_stpack_a;
stpack_t v_stpack_a1;
simple_a_t v_simple_a;
simple_a1_t v_simple_a1;
int v_unpk_a[2][3];
int v_unpk_a1[2][3];
int v_assoc_a[string];
int v_assoc_a1[string];
int v_assoc_b[int];
int v_assoc_c[bit[31:0]];
int v_q_a[$];
int v_q_a1[$];
real v_q_b[$];
bit [3:0][7:0] v_2thirtytwo_a;
bit [3:0][7:0] v_2thirtytwo_b;
logic [3:0][7:0] v_4thirtytwo_a;
logic [3:0][7:0] v_4thirtytwo_b;
Base v_cls_a;
Base v_cls_a1;
BaseExtended v_cls_ab;
Other v_cls_b;
// verilator lint_off REALCVT
initial begin
// 6.22.1
`MATCHING(v_real, v_real);
`MATCHING(v_string, v_string);
`MATCHING(v_int, v_int);
`MATCHING(v_chandle, v_chandle);
`MATCHING(v_int, v_int_t);
`MATCHING(v_stpack_a, v_stpack_a1);
`MATCHING(v_simple_a, v_simple_a1);
`MATCHING(v_unpk_a, v_unpk_a1);
`MATCHING(v_assoc_a, v_assoc_a1);
`MATCHING(v_q_a, v_q_a1);
`MATCHING(v_int, v_2thirtytwo_a);
`MATCHING(v_cls_a, v_cls_a1);
`MATCHING(v_cls_a, v_cls_ab);
// 6.22.2
`EQUIVALENT(v_int, v_2thirtytwo_a);
`ifndef NC
`ifndef VCS
`EQUIVALENT(v_assoc_b, v_assoc_c); // Spec says equivalent, but simulators disagree
`endif
`endif
// 6.22.3
`COMPATIBLE(v_string, `STRING_LITERAL);
`COMPATIBLE(v_int, v_enum_a);
`COMPATIBLE(v_int, v_real);
`COMPATIBLE(v_real, v_int);
// 6.22.4->5.9
`ifndef NC
`CAST_COMPATIBLE(v_string, v_int);
`endif
// 6.22.4->6.19.3
`ifndef NC
`CAST_COMPATIBLE_ENUM(v_enum_a, v_int);
`CAST_COMPATIBLE_ENUM(v_enum_a, v_enum_b);
`endif
`CAST_COMPATIBLE_DYNAMIC(v_cls_ab, v_cls_a);
// 6.22.5 incompatible
`INCOMPATIBLE(v_cls_ab, v_int);
`ifndef VCS
`INCOMPATIBLE(v_real, v_assoc_a);
`INCOMPATIBLE(v_real, v_q_a);
`endif
`ifndef VCS
`ifndef VERILATOR
`INCOMPATIBLE(v_chandle, v_int);
`endif
`endif
`ifndef NC
`INCOMPATIBLE(v_cls_a, v_cls_b);
`endif
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -1,29 +0,0 @@
%Error-UNSUPPORTED: t/t_castdyn.v:28:11: Unsupported: $cast. Suggest try static cast.
: ... In instance t
28 | i = $cast(ao, a);
| ^~~~~
%Error-UNSUPPORTED: t/t_castdyn.v:33:7: Unsupported: $cast. Suggest try static cast.
: ... In instance t
33 | $cast(ao, a);
| ^~~~~
%Error-UNSUPPORTED: t/t_castdyn.v:36:11: Unsupported: $cast. Suggest try static cast.
: ... In instance t
36 | i = $cast(ao, 2.1 * 3.7);
| ^~~~~
%Error-UNSUPPORTED: t/t_castdyn.v:40:11: Unsupported: $cast. Suggest try static cast.
: ... In instance t
40 | i = $cast(bo, null);
| ^~~~~
%Error-UNSUPPORTED: t/t_castdyn.v:46:11: Unsupported: $cast. Suggest try static cast.
: ... In instance t
46 | i = $cast(bao, b);
| ^~~~~
%Error-UNSUPPORTED: t/t_castdyn.v:52:11: Unsupported: $cast. Suggest try static cast.
: ... In instance t
52 | i = $cast(bbo, b);
| ^~~~~
%Error-UNSUPPORTED: t/t_castdyn.v:59:11: Unsupported: $cast. Suggest try static cast.
: ... In instance t
59 | i = $cast(bao, b);
| ^~~~~
%Error: Exiting due to

View File

@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
);
ok(1);
1;

View File

@ -23,6 +23,8 @@ module t (/*AUTOARG*/);
BasedB bb;
BasedB bbo;
// verilator lint_off CASTCONST
initial begin
a = 1234;
i = $cast(ao, a);

View File

@ -0,0 +1,16 @@
%Warning-CASTCONST: t/t_castdyn_castconst_bad.v:20:11: $cast will always return one as 'int' is always castable from 'logic[31:0]'
: ... In instance t
: ... Suggest static cast
20 | i = $cast(v, 1);
| ^~~~~
... Use "/* verilator lint_off CASTCONST */" and lint_on around source to disable this message.
%Warning-CASTCONST: t/t_castdyn_castconst_bad.v:21:11: $cast will always return one as 'CLASSREFDTYPE 'Base'' is always castable from 'CLASSREFDTYPE 'Base''
: ... In instance t
: ... Suggest static cast
21 | i = $cast(b, b);
| ^~~~~
%Warning-CASTCONST: t/t_castdyn_castconst_bad.v:22:11: $cast will always return zero as 'CLASSREFDTYPE 'Base'' is not castable from 'CLASSREFDTYPE 'Other''
: ... In instance t
22 | i = $cast(b, o);
| ^~~~~
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003-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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(linter => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,27 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2005-2007 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
class Base;
endclass
class Other;
endclass
enum { ZERO } e;
module t (/*AUTOARG*/);
int i;
int v;
Base b;
Other o;
initial begin
i = $cast(v, 1); // 1
i = $cast(b, b); // 1
i = $cast(b, o); // 0
i = $cast(e, 0); // 1
i = $cast(e, 10); // 0
end
endmodule

View File

@ -1,5 +0,0 @@
%Error-UNSUPPORTED: t/t_castdyn_enum.v:23:11: Unsupported: $cast. Suggest try static cast.
: ... In instance t
23 | i = $cast(en, cyc);
| ^~~~~
%Error: Exiting due to

View File

@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
);
ok(1);
1;

View File

@ -15,9 +15,23 @@ module t (/*AUTOARG*/
input clk;
int i;
int i_const;
int cyc;
enum_t en;
// Constant propagation tests
initial begin
en = SIXTEEN;
i_const = $cast(en, 1);
if (i_const != 0) $stop;
if (en != SIXTEEN) $stop;
en = SIXTEEN;
i_const = $cast(en, 10);
if (i_const != 1) $stop;
if (en != TEN) $stop;
end
// Test loop
always @ (posedge clk) begin
i = $cast(en, cyc);

View File

@ -1,9 +1,3 @@
%Error-UNSUPPORTED: t/t_castdyn_run_bad.v:20:11: Unsupported: $cast. Suggest try static cast.
: ... In instance t
20 | i = $cast(c, b);
| ^~~~~
%Error-UNSUPPORTED: t/t_castdyn_run_bad.v:23:7: Unsupported: $cast. Suggest try static cast.
: ... In instance t
23 | $cast(c, b);
| ^~~~~
%Error: Exiting due to
[0] %Error: t_castdyn_run_bad.v:32: Assertion failed in top.t: 'assert' failed.
%Error: t/t_castdyn_run_bad.v:32: Verilog $stop
Aborting...

View File

@ -11,13 +11,12 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -6,21 +6,30 @@
class Base;
endclass
class C;
class ExbaseA extends Base;
endclass
class ExbaseB extends Base;
endclass
module t (/*AUTOARG*/);
int i;
Base b;
C c;
ExbaseA ba, ba1;
ExbaseB bb, bb1;
initial begin
b = new;
i = $cast(c, b);
if (i != 0) $stop;
ba = new;
b = ba;
i = $cast(ba1, b);
if (i != 1) $stop;
$cast(ba1, b); // ok at runtime
$cast(c, b); // Bad at runtime
bb = new;
b = bb;
i = $cast(ba1, b);
if (i != 0) $stop;
$cast(ba1, b);
$write("*-* All Finished *-*\n");
$finish;

View File

@ -0,0 +1,6 @@
%Error-UNSUPPORTED: t/t_castdyn_unsup_bad.v:13:7: Unsupported: $cast to 'string[$]' from 'int[string]'
: ... In instance t
: ... Suggest try static cast
13 | $cast(q, aarray);
| ^~~~~
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003-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.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(linter => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,16 @@
// 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
module t (/*AUTOARG*/);
string q[$];
int aarray[string];
initial begin
$cast(q, aarray);
end
endmodule