Merge 208114dced
into 078bb21a89
This commit is contained in:
commit
b5ccebd920
|
@ -158,6 +158,7 @@ Martin Schmidt
|
|||
Martin Stadler
|
||||
Mateusz Gancarz
|
||||
Matthew Ballance
|
||||
Maxim Fonarev
|
||||
Michael Bikovitsky
|
||||
Michael Killough
|
||||
Michal Czyz
|
||||
|
|
|
@ -1823,6 +1823,15 @@ public:
|
|||
void deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE;
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Represents the null pointer. Used in VlClassRef and VlIfaceRef for:
|
||||
// * setting to null instead of via nullptr_t, to prevent the implicit conversion of 0 to nullptr
|
||||
// * comparing pointers to null.
|
||||
|
||||
struct VlNull final {
|
||||
operator bool() const { return false; }
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Base class for all verilated classes. Includes a reference counter, and a pointer to the deleter
|
||||
// object that should destroy it after the counter reaches 0. This allows for easy construction of
|
||||
|
@ -1856,18 +1865,6 @@ public:
|
|||
~VlClass() override = default;
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// Represents the null pointer. Used for:
|
||||
// * setting VlClassRef to null instead of via nullptr_t, to prevent the implicit conversion of 0
|
||||
// to nullptr,
|
||||
// * comparing interface pointers to null.
|
||||
|
||||
struct VlNull final {
|
||||
operator bool() const { return false; }
|
||||
bool operator==(const void* ptr) const { return !ptr; }
|
||||
};
|
||||
inline bool operator==(const void* ptr, VlNull) { return !ptr; }
|
||||
|
||||
//===================================================================
|
||||
// Verilog class reference container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
|
@ -2015,6 +2012,130 @@ static inline bool VL_CAST_DYNAMIC(VlNull in, VlClassRef<T_Lhs>& outr) {
|
|||
return true;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Verilog interface reference container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
// be protected by other means
|
||||
|
||||
template <typename T_Iface>
|
||||
class VlIfaceRef final {
|
||||
private:
|
||||
// TYPES
|
||||
template <typename T_OtherIface>
|
||||
friend class VlIfaceRef; // Needed for template copy/move assignments
|
||||
|
||||
// MEMBERS
|
||||
T_Iface* m_objp = nullptr; // Object pointed to
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
VlIfaceRef() = default;
|
||||
VlIfaceRef(VlNull){};
|
||||
explicit VlIfaceRef(T_Iface* objp)
|
||||
: m_objp{objp} {}
|
||||
VlIfaceRef(const VlIfaceRef& copied)
|
||||
: m_objp{copied.m_objp} {}
|
||||
VlIfaceRef(VlIfaceRef&& moved)
|
||||
: m_objp{std::exchange(moved.m_objp, nullptr)} {}
|
||||
template <typename T_OtherIface>
|
||||
VlIfaceRef(const VlIfaceRef<T_OtherIface>& copied)
|
||||
: m_objp{copied.m_objp} {}
|
||||
template <typename T_OtherIface>
|
||||
VlIfaceRef(VlIfaceRef<T_OtherIface>&& moved)
|
||||
: m_objp{std::exchange(moved.m_objp, nullptr)} {}
|
||||
~VlIfaceRef() {}
|
||||
|
||||
// METHODS
|
||||
VlIfaceRef& operator=(T_Iface* copied) {
|
||||
if (m_objp == copied) return *this;
|
||||
m_objp = copied;
|
||||
return *this;
|
||||
}
|
||||
VlIfaceRef& operator=(const VlIfaceRef& copied) {
|
||||
if (m_objp == copied.m_objp) return *this;
|
||||
m_objp = copied.m_objp;
|
||||
return *this;
|
||||
}
|
||||
VlIfaceRef& operator=(VlIfaceRef&& moved) {
|
||||
if (m_objp == moved.m_objp) return *this;
|
||||
m_objp = std::exchange(moved.m_objp, nullptr);
|
||||
return *this;
|
||||
}
|
||||
template <typename T_OtherIface>
|
||||
VlIfaceRef& operator=(const VlIfaceRef<T_OtherIface>& copied) {
|
||||
if (m_objp == copied.m_objp) return *this;
|
||||
m_objp = copied.m_objp;
|
||||
return *this;
|
||||
}
|
||||
template <typename T_OtherIface>
|
||||
VlIfaceRef& operator=(VlIfaceRef<T_OtherIface>&& moved) {
|
||||
if (m_objp == moved.m_objp) return *this;
|
||||
m_objp = std::exchange(moved.m_objp, nullptr);
|
||||
return *this;
|
||||
}
|
||||
// Assign with nullptr
|
||||
VlIfaceRef& operator=(VlNull) {
|
||||
m_objp = nullptr;
|
||||
return *this;
|
||||
}
|
||||
// Dynamic caster
|
||||
template <typename T_OtherIface>
|
||||
VlIfaceRef<T_OtherIface> dynamicCast() const {
|
||||
return VlIfaceRef<T_OtherIface>{dynamic_cast<T_OtherIface*>(m_objp)};
|
||||
}
|
||||
// Dereference operators
|
||||
T_Iface& operator*() const { return *m_objp; }
|
||||
T_Iface* operator->() const { return m_objp; }
|
||||
// For 'if (ptr)...'
|
||||
operator bool() const { return m_objp; }
|
||||
// In SV A == B iff both are handles to the same object (IEEE 1800-2023 8.4)
|
||||
template <typename T_OtherIface>
|
||||
bool operator==(const VlIfaceRef<T_OtherIface>& rhs) const {
|
||||
return m_objp == rhs.m_objp;
|
||||
};
|
||||
template <typename T_OtherIface>
|
||||
bool operator!=(const VlIfaceRef<T_OtherIface>& rhs) const {
|
||||
return m_objp != rhs.m_objp;
|
||||
};
|
||||
template <typename T_OtherIface>
|
||||
bool operator<(const VlIfaceRef<T_OtherIface>& rhs) const {
|
||||
return m_objp < rhs.m_objp;
|
||||
};
|
||||
template <typename T_OtherIface>
|
||||
bool operator==(const T_OtherIface & rhs) const {
|
||||
return m_objp == rhs;
|
||||
};
|
||||
template <typename T_OtherIface>
|
||||
bool operator!=(const T_OtherIface & rhs) const {
|
||||
return m_objp != rhs;
|
||||
};
|
||||
template <typename T_OtherIface>
|
||||
bool operator<(const T_OtherIface & rhs) const {
|
||||
return m_objp < rhs;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T_Lhs, typename T_Out>
|
||||
static inline bool VL_CAST_DYNAMIC(VlIfaceRef<T_Lhs> in, VlIfaceRef<T_Out>& outr) {
|
||||
if (!in) {
|
||||
outr = VlNull{};
|
||||
return true;
|
||||
}
|
||||
VlIfaceRef<T_Out> casted = in.template dynamicCast<T_Out>();
|
||||
if (VL_LIKELY(casted)) {
|
||||
outr = casted;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T_Lhs>
|
||||
static inline bool VL_CAST_DYNAMIC(VlNull in, VlIfaceRef<T_Lhs>& outr) {
|
||||
outr = VlNull{};
|
||||
return true;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// VlSampleQueue stores samples for input clockvars in clocking blocks. At a clocking event,
|
||||
// samples from this queue should be written to the correct input clockvar.
|
||||
|
|
|
@ -942,7 +942,7 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
|||
info.m_type = "VlClassRef<" + EmitCBase::prefixNameProtect(adtypep) + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
info.m_type = EmitCBase::prefixNameProtect(adtypep->ifaceViaCellp()) + "*";
|
||||
info.m_type = "VlIfaceRef<" + EmitCBase::prefixNameProtect(adtypep->ifaceViaCellp()) + ">";
|
||||
} else if (const auto* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
UASSERT_OBJ(!packed, this, "Unsupported type for packed struct or union");
|
||||
if (adtypep->isCompound()) compound = true;
|
||||
|
|
|
@ -66,7 +66,7 @@ static void makeVlToString(AstClass* nodep) {
|
|||
static void makeVlToString(AstIface* nodep) {
|
||||
AstCFunc* const funcp
|
||||
= new AstCFunc{nodep->fileline(), "VL_TO_STRING", nullptr, "std::string"};
|
||||
funcp->argTypes("const " + EmitCBase::prefixNameProtect(nodep) + "* obj");
|
||||
funcp->argTypes("const VlIfaceRef<" + EmitCBase::prefixNameProtect(nodep) + ">& obj");
|
||||
funcp->isMethod(false);
|
||||
funcp->isConst(false);
|
||||
funcp->isStatic(false);
|
||||
|
|
|
@ -693,7 +693,7 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
|
|||
} else if (VN_IS(dtypep, ClassRefDType)) {
|
||||
return ""; // Constructor does it
|
||||
} else if (VN_IS(dtypep, IfaceRefDType)) {
|
||||
return varNameProtected + suffix + " = nullptr;\n";
|
||||
return ""; // Constructor does it
|
||||
} else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
// Access std::array as C array
|
||||
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
||||
|
|
|
@ -7578,30 +7578,40 @@ class WidthVisitor final : public VNVisitor {
|
|||
<< underp->prettyNameQ() << " is an interface.");
|
||||
} else if (const AstIfaceRefDType* expIfaceRefp
|
||||
= VN_CAST(expDTypep->skipRefp(), IfaceRefDType)) {
|
||||
const AstIfaceRefDType* underIfaceRefp
|
||||
= VN_CAST(underp->dtypep()->skipRefp(), IfaceRefDType);
|
||||
if (!underIfaceRefp) {
|
||||
underp->v3error(ucfirst(nodep->prettyOperatorName())
|
||||
<< " expected " << expIfaceRefp->ifaceViaCellp()->prettyNameQ()
|
||||
<< " interface on " << side << " but " << underp->prettyNameQ()
|
||||
<< " is not an interface.");
|
||||
} else if (expIfaceRefp->ifaceViaCellp() != underIfaceRefp->ifaceViaCellp()) {
|
||||
underp->v3error(ucfirst(nodep->prettyOperatorName())
|
||||
<< " expected " << expIfaceRefp->ifaceViaCellp()->prettyNameQ()
|
||||
<< " interface on " << side << " but " << underp->prettyNameQ()
|
||||
<< " is a different interface ("
|
||||
<< underIfaceRefp->ifaceViaCellp()->prettyNameQ() << ").");
|
||||
} else if (underIfaceRefp->modportp()
|
||||
&& expIfaceRefp->modportp() != underIfaceRefp->modportp()) {
|
||||
underp->v3error(ucfirst(nodep->prettyOperatorName())
|
||||
<< " expected "
|
||||
<< (expIfaceRefp->modportp()
|
||||
? expIfaceRefp->modportp()->prettyNameQ()
|
||||
: "no")
|
||||
<< " interface modport on " << side << " but got "
|
||||
<< underIfaceRefp->modportp()->prettyNameQ() << " modport.");
|
||||
if (VN_IS(underp, Const)) {
|
||||
if (!VN_CAST(underp, Const)->num().isNull()) {
|
||||
underp->v3error(ucfirst(nodep->prettyOperatorName())
|
||||
<< " expected 'null' keyword"
|
||||
<< " on " << side << " but got " << underp->prettyNameQ());
|
||||
} else {
|
||||
underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p());
|
||||
}
|
||||
} else {
|
||||
underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p());
|
||||
const AstIfaceRefDType* underIfaceRefp
|
||||
= VN_CAST(underp->dtypep()->skipRefp(), IfaceRefDType);
|
||||
if (!underIfaceRefp) {
|
||||
underp->v3error(ucfirst(nodep->prettyOperatorName())
|
||||
<< " expected " << expIfaceRefp->ifaceViaCellp()->prettyNameQ()
|
||||
<< " interface on " << side << " but " << underp->prettyNameQ()
|
||||
<< " is not an interface.");
|
||||
} else if (expIfaceRefp->ifaceViaCellp() != underIfaceRefp->ifaceViaCellp()) {
|
||||
underp->v3error(ucfirst(nodep->prettyOperatorName())
|
||||
<< " expected " << expIfaceRefp->ifaceViaCellp()->prettyNameQ()
|
||||
<< " interface on " << side << " but " << underp->prettyNameQ()
|
||||
<< " is a different interface ("
|
||||
<< underIfaceRefp->ifaceViaCellp()->prettyNameQ() << ").");
|
||||
} else if (underIfaceRefp->modportp()
|
||||
&& expIfaceRefp->modportp() != underIfaceRefp->modportp()) {
|
||||
underp->v3error(ucfirst(nodep->prettyOperatorName())
|
||||
<< " expected "
|
||||
<< (expIfaceRefp->modportp()
|
||||
? expIfaceRefp->modportp()->prettyNameQ()
|
||||
: "no")
|
||||
<< " interface modport on " << side << " but got "
|
||||
<< underIfaceRefp->modportp()->prettyNameQ() << " modport.");
|
||||
} else {
|
||||
underp = userIterateSubtreeReturnEdits(underp, WidthVP{expDTypep, FINAL}.p());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Hope it just works out (perhaps a cast will deal with it)
|
||||
|
|
|
@ -32,10 +32,17 @@ module t (/*AUTOARG*/);
|
|||
Clsgen#(virtual PBus) gen;
|
||||
|
||||
initial begin
|
||||
if (va != null) $stop;
|
||||
va = null;
|
||||
if (va != null) $stop;
|
||||
va = ia;
|
||||
vb = ia;
|
||||
|
||||
if (va == null) $stop;
|
||||
va = null;
|
||||
if (va != null) $stop;
|
||||
va = ia;
|
||||
if (va == null) $stop;
|
||||
|
||||
vb = ia;
|
||||
|
||||
$display("va==vb? %b", va==vb);
|
||||
$display("va!=vb? %b", va!=vb);
|
||||
|
|
Loading…
Reference in New Issue