This commit is contained in:
Maxim Fonarev 2025-07-20 21:43:27 +08:00 committed by GitHub
commit b5ccebd920
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 179 additions and 40 deletions

View File

@ -158,6 +158,7 @@ Martin Schmidt
Martin Stadler
Mateusz Gancarz
Matthew Ballance
Maxim Fonarev
Michael Bikovitsky
Michael Killough
Michal Czyz

View File

@ -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.

View File

@ -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;

View File

@ -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);

View File

@ -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()" : "");

View File

@ -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)

View File

@ -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);