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 * Verilator 4.107 devel
*** Support $cast and new CASTCONST warning.
*** Add --top option as alias of --top-module. *** Add --top option as alias of --top-module.
**** Fix passing parameter type instantiations by position number. **** 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 Disable all lint related warning messages, and all style warnings. This is
equivalent to "-Wno-ALWCOMBORDER -Wno-BSSPACE -Wno-CASEINCOMPLETE 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-ENDLABEL -Wno-IMPLICIT -Wno-LITENDIAN -Wno-PINCONNECTEMPTY
-Wno-PINMISSING -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED -Wno-UNUSED -Wno-PINMISSING -Wno-SYNCASYNCNET -Wno-UNDRIVEN -Wno-UNSIGNED -Wno-UNUSED
-Wno-WIDTH" plus the list shown for Wno-style. -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 Enable all lint related warning messages (note by default they are already
enabled), but do not affect style messages. This is equivalent to enabled), but do not affect style messages. This is equivalent to
"-Wwarn-ALWCOMBORDER -Wwarn-BSSPACE -Wwarn-CASEINCOMPLETE "-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-COLONPLUS -Wwarn-ENDLABEL -Wwarn-IMPLICIT -Wwarn-LITENDIAN
-Wwarn-PINMISSING -Wwarn-REALCVT -Wwarn-UNSIGNED -Wwarn-WIDTH". -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 Ignoring this warning will only suppress the lint check, it will simulate
correctly. 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 =item CASEWITHX
Warns that a case statement contains a constant with a C<x>. Verilator is 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 Ignoring this warning will only suppress the lint check, it will simulate
correctly. 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 =item CDCRSTLOGIC
With --cdc only, warns that asynchronous flop reset terms come from other 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; 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 // Conversion functions

View File

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

View File

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

View File

@ -3460,6 +3460,30 @@ public:
virtual bool brokeLhsMustBeLvalue() const override { return true; } 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 { class AstComment final : public AstNodeStmt {
// Some comment to put into the output stream // Some comment to put into the output stream
// Parents: {statement list} // Parents: {statement list}
@ -6050,9 +6074,8 @@ public:
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override { virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override {
return new AstCastDynamic(this->fileline(), lhsp, rhsp); return new AstCastDynamic(this->fileline(), lhsp, rhsp);
} }
virtual string emitVerilog() override { return "%f$cast(%l, %r)"; } virtual string emitVerilog() override { return "%f$cast(%r, %l)"; }
// Non-existent filehandle returns EOF virtual string emitC() override { return "VL_DYNAMIC_CAST(%r, %l)"; }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual bool cleanOut() const override { return true; } virtual bool cleanOut() const override { return true; }
virtual bool cleanLhs() const override { return true; } virtual bool cleanLhs() const override { return true; }
virtual bool cleanRhs() 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 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 { class AstCover final : public AstNodeCoverOrAssert {
public: public:
ASTNODE_NODE_FUNCS(Cover) ASTNODE_NODE_FUNCS(Cover)

View File

@ -818,6 +818,15 @@ public:
} }
puts("}\n"); 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 { virtual void visit(AstStop* nodep) override {
puts("VL_STOP_MT("); puts("VL_STOP_MT(");
putsQuoted(protect(nodep->fileline()->filename())); 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(), emitOpName(nodep, "VL_STREAML_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)", nodep->lhsp(),
nodep->rhsp(), nullptr); 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 { virtual void visit(AstCountBits* nodep) override {
putbs("VL_COUNTBITS_"); putbs("VL_COUNTBITS_");
emitIQW(nodep->lhsp()); emitIQW(nodep->lhsp());

View File

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

View File

@ -100,6 +100,12 @@ std::ostream& operator<<(std::ostream& str, const Determ& rhs) {
return str << s_det[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 // Width state, as a visitor of each AstNode
@ -1626,11 +1632,70 @@ private:
} }
virtual void visit(AstCastDynamic* nodep) override { virtual void visit(AstCastDynamic* nodep) override {
nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED); // Spec says integer return nodep->dtypeChgWidthSigned(32, 1, VSigning::SIGNED); // Spec says integer return
nodep->v3warn(E_UNSUPPORTED, "Unsupported: $cast. Suggest try static cast."); userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
AstNode* newp = new AstConst(nodep->fileline(), 1); 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); newp->dtypeFrom(nodep);
nodep->replaceWith(newp); nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep); VL_DO_DANGLING(pushDeletep(nodep), nodep);
userIterate(newp, m_vup);
} }
virtual void visit(AstCastParse* nodep) override { virtual void visit(AstCastParse* nodep) override {
// nodep->dtp could be data type, or a primary_constant // nodep->dtp could be data type, or a primary_constant
@ -1651,53 +1716,86 @@ private:
} }
virtual void visit(AstCast* nodep) override { virtual void visit(AstCast* nodep) override {
nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); 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 (m_vup->prelim()) {
// if (debug()) nodep->dumpTree(cout, " CastPre: ");
userIterateAndNext(nodep->fromp(), WidthVP(SELF, PRELIM).p()); userIterateAndNext(nodep->fromp(), WidthVP(SELF, PRELIM).p());
// When implement more complicated types need to convert childDTypep to AstNodeDType* toDtp = nodep->dtypep()->skipRefToEnump();
// dtypep() not as a child AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
if (!basicp->isDouble() && !nodep->fromp()->isDouble()) { const auto castable = computeCastable(toDtp, fromDtp, nodep->fromp());
// Note castSized might modify nodep->fromp() bool bad = false;
int width = nodep->dtypep()->width(); if (castable == UNSUPPORTED) {
castSized(nodep, nodep->fromp(), width); 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 { } else {
iterateCheck(nodep, "value", nodep->fromp(), SELF, FINAL, nodep->fromp()->dtypep(), nodep->v3fatalSrc("bad casting case");
EXTEND_EXP, false);
} }
AstNode* newp = nodep->fromp()->unlinkFrBack(); // For now, replace it ASAP, so widthing can propagate easily
if (basicp->isDouble() && !newp->isDouble()) { // The cast may change signing, but we don't know the sign yet. Make it so.
if (newp->isSigned()) { // Note we don't sign fromp() that would make the algorithm O(n^2) if lots of casting.
newp = new AstISToRD(nodep->fileline(), newp); 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 { } 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->isDouble() && !nodep->fromp()->isDouble()) {
if (basicp->isSigned()) { if (nodep->fromp()->isSigned()) {
newp = new AstRToIRoundS(nodep->fileline(), newp); 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 { } else {
newp = new AstUnsigned(nodep->fileline(), // Can just remove cast
new AstRToIS(nodep->fileline(), newp));
} }
} else if (basicp->isSigned() && !newp->isSigned()) { } else if (VN_IS(toDtp, ClassRefDType)) {
newp = new AstSigned(nodep->fileline(), newp); // Can just remove cast
} else if (!basicp->isSigned() && newp->isSigned()) {
newp = new AstUnsigned(nodep->fileline(), newp);
} else { } 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); 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()) { if (m_vup->final()) {
iterateCheck(nodep, "value", nodep->lhsp(), SELF, FINAL, nodep->lhsp()->dtypep(),
EXTEND_EXP, false);
AstNode* underp = nodep->lhsp()->unlinkFrBack(); AstNode* underp = nodep->lhsp()->unlinkFrBack();
if (debug()) underp->dumpTree(cout, " CastRep: ");
nodep->replaceWith(underp); nodep->replaceWith(underp);
VL_DO_DANGLING(pushDeletep(nodep), nodep); VL_DO_DANGLING(pushDeletep(nodep), nodep);
} }
@ -3603,6 +3701,12 @@ private:
iterateCheckBool(nodep, "If", nodep->condp(), BOTH); // it's like an if() condition. iterateCheckBool(nodep, "If", nodep->condp(), BOTH); // it's like an if() condition.
// if (debug()) nodep->dumpTree(cout, " IfOut: "); // 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 { 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 // 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->passsp(), nullptr);
userIterateAndNext(nodep->failsp(), 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 { virtual void visit(AstCover* nodep) override {
assertAtStatement(nodep); assertAtStatement(nodep);
iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition. iterateCheckBool(nodep, "Property", nodep->propp(), BOTH); // it's like an if() condition.
@ -5730,6 +5840,12 @@ private:
AstNodeDType* basep; AstNodeDType* basep;
if (attrType == AstAttrType::ENUM_NAME) { if (attrType == AstAttrType::ENUM_NAME) {
basep = nodep->findStringDType(); 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 { } else {
basep = nodep->dtypep(); basep = nodep->dtypep();
} }
@ -5752,6 +5868,8 @@ private:
initp->defaultp(new AstConst(nodep->fileline(), AstConst::String(), "")); initp->defaultp(new AstConst(nodep->fileline(), AstConst::String(), ""));
} else if (attrType == AstAttrType::ENUM_NEXT || attrType == AstAttrType::ENUM_PREV) { } else if (attrType == AstAttrType::ENUM_NEXT || attrType == AstAttrType::ENUM_PREV) {
initp->defaultp(new AstConst(nodep->fileline(), V3Number(nodep, nodep->width(), 0))); 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 { } else {
nodep->v3fatalSrc("Bad case"); nodep->v3fatalSrc("Bad case");
} }
@ -5776,6 +5894,8 @@ private:
values[i] = (nextp ? nextp : firstp)->valuep()->cloneTree(false); // A const values[i] = (nextp ? nextp : firstp)->valuep()->cloneTree(false); // A const
} else if (attrType == AstAttrType::ENUM_PREV) { } else if (attrType == AstAttrType::ENUM_PREV) {
values[i] = prevp->valuep()->cloneTree(false); // A const values[i] = prevp->valuep()->cloneTree(false); // A const
} else if (attrType == AstAttrType::ENUM_VALID) {
values[i] = new AstConst(nodep->fileline(), AstConst::BitTrue{});
} else { } else {
nodep->v3fatalSrc("Bad case"); nodep->v3fatalSrc("Bad case");
} }
@ -5785,8 +5905,7 @@ private:
} }
// Add all specified values to table // Add all specified values to table
for (unsigned i = 0; i < (msbdim + 1); ++i) { for (unsigned i = 0; i < (msbdim + 1); ++i) {
AstNode* valp = values[i]; if (values[i]) initp->addIndexValuep(i, values[i]);
if (valp) initp->addIndexValuep(i, valp);
} }
userIterate(varp, nullptr); // May have already done $unit so must do this var userIterate(varp, nullptr); // May have already done $unit so must do this var
m_tableMap.insert(make_pair(make_pair(nodep, attrType), varp)); m_tableMap.insert(make_pair(make_pair(nodep, attrType), varp));
@ -5860,6 +5979,48 @@ private:
return false; 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 // METHODS - special type detection
void assertAtStatement(AstNode* nodep) { 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_WRITEMEMH '(' expr ',' idClassSel ',' expr ',' expr ')' { $$ = new AstWriteMem($1, true, $3, $5, $7, $9); }
// //
| yD_CAST '(' expr ',' expr ')' | 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 // Any system function as a task
| system_f_call_or_t { $$ = new AstSysFuncAsTask($<fl>1, $1); } | system_f_call_or_t { $$ = new AstSysFuncAsTask($<fl>1, $1); }

View File

@ -768,7 +768,7 @@ sub _exit {
if ($self->ok) { if ($self->ok) {
$self->oprint("Self PASSED\n"); $self->oprint("Self PASSED\n");
} elsif ($self->skips && !$self->errors) { } elsif ($self->skips && !$self->errors) {
$self->oprint("%Skip: $self->{skips}\n"); $self->oprint("-Skip: $self->{skips}\n");
} elsif ($self->unsupporteds && !$self->errors) { } elsif ($self->unsupporteds && !$self->errors) {
$self->oprint("%Unsupported: $self->{unsupporteds}\n"); $self->oprint("%Unsupported: $self->{unsupporteds}\n");
} else { } 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); scenarios(simulator => 1);
compile( compile(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
); );
execute( execute(
check_finished => 1, check_finished => 1,
) if !$Self->{vlt_all}; );
ok(1); ok(1);
1; 1;

View File

@ -23,6 +23,8 @@ module t (/*AUTOARG*/);
BasedB bb; BasedB bb;
BasedB bbo; BasedB bbo;
// verilator lint_off CASTCONST
initial begin initial begin
a = 1234; a = 1234;
i = $cast(ao, a); 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); scenarios(simulator => 1);
compile( compile(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
); );
execute( execute(
check_finished => 1, check_finished => 1,
) if !$Self->{vlt_all}; );
ok(1); ok(1);
1; 1;

View File

@ -15,9 +15,23 @@ module t (/*AUTOARG*/
input clk; input clk;
int i; int i;
int i_const;
int cyc; int cyc;
enum_t en; 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 // Test loop
always @ (posedge clk) begin always @ (posedge clk) begin
i = $cast(en, cyc); 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. [0] %Error: t_castdyn_run_bad.v:32: Assertion failed in top.t: 'assert' failed.
: ... In instance t %Error: t/t_castdyn_run_bad.v:32: Verilog $stop
20 | i = $cast(c, b); Aborting...
| ^~~~~
%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

View File

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

View File

@ -6,21 +6,30 @@
class Base; class Base;
endclass endclass
class C; class ExbaseA extends Base;
endclass
class ExbaseB extends Base;
endclass endclass
module t (/*AUTOARG*/); module t (/*AUTOARG*/);
int i; int i;
Base b; Base b;
C c; ExbaseA ba, ba1;
ExbaseB bb, bb1;
initial begin initial begin
b = new; ba = new;
i = $cast(c, b); b = ba;
if (i != 0) $stop; 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"); $write("*-* All Finished *-*\n");
$finish; $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