forked from github/verilator
Support $cast and new CASTCONST warning.
This commit is contained in:
parent
8b8ebb0e43
commit
74ef35d3b3
2
Changes
2
Changes
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
|
@ -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 {
|
||||
|
235
src/V3Width.cpp
235
src/V3Width.cpp
@ -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) {
|
||||
|
@ -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); }
|
||||
|
@ -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
21
test_regress/t/t_cast_class.pl
Executable 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;
|
39
test_regress/t/t_cast_class.v
Normal file
39
test_regress/t/t_cast_class.v
Normal 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
|
10
test_regress/t/t_cast_class_incompat_bad.out
Normal file
10
test_regress/t/t_cast_class_incompat_bad.out
Normal 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
|
19
test_regress/t/t_cast_class_incompat_bad.pl
Executable file
19
test_regress/t/t_cast_class_incompat_bad.pl
Executable 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;
|
30
test_regress/t/t_cast_class_incompat_bad.v
Normal file
30
test_regress/t/t_cast_class_incompat_bad.v
Normal 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
21
test_regress/t/t_cast_types.pl
Executable 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;
|
136
test_regress/t/t_cast_types.v
Normal file
136
test_regress/t/t_cast_types.v
Normal 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
|
@ -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
|
@ -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;
|
||||
|
@ -23,6 +23,8 @@ module t (/*AUTOARG*/);
|
||||
BasedB bb;
|
||||
BasedB bbo;
|
||||
|
||||
// verilator lint_off CASTCONST
|
||||
|
||||
initial begin
|
||||
a = 1234;
|
||||
i = $cast(ao, a);
|
||||
|
16
test_regress/t/t_castdyn_castconst_bad.out
Normal file
16
test_regress/t/t_castdyn_castconst_bad.out
Normal 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
|
19
test_regress/t/t_castdyn_castconst_bad.pl
Executable file
19
test_regress/t/t_castdyn_castconst_bad.pl
Executable 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;
|
27
test_regress/t/t_castdyn_castconst_bad.v
Normal file
27
test_regress/t/t_castdyn_castconst_bad.v
Normal 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
|
@ -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
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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...
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
6
test_regress/t/t_castdyn_unsup_bad.out
Normal file
6
test_regress/t/t_castdyn_unsup_bad.out
Normal 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
|
19
test_regress/t/t_castdyn_unsup_bad.pl
Executable file
19
test_regress/t/t_castdyn_unsup_bad.pl
Executable 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;
|
16
test_regress/t/t_castdyn_unsup_bad.v
Normal file
16
test_regress/t/t_castdyn_unsup_bad.v
Normal 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
|
Loading…
Reference in New Issue
Block a user