diff --git a/include/verilated.cpp b/include/verilated.cpp index a50023a77..fe450ab25 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -70,7 +70,12 @@ VL_THREAD_LOCAL Verilated::ThreadLocal Verilated::t_s; Verilated::CommandArgValues Verilated::s_args; -VerilatedImp VerilatedImp::s_s; +VerilatedImp::VerilatedImpU VerilatedImp::s_s; + +struct VerilatedImpInitializer { + VerilatedImpInitializer() { VerilatedImp::setup(); } + ~VerilatedImpInitializer() { VerilatedImp::teardown(); } +} g_VerilatedImpInitializer; //=========================================================================== // User definable functions @@ -265,8 +270,8 @@ Verilated::NonSerialized::~NonSerialized() { } } -size_t Verilated::serialized2Size() VL_PURE { return sizeof(VerilatedImp::s_s.m_ser); } -void* Verilated::serialized2Ptr() VL_MT_UNSAFE { return &VerilatedImp::s_s.m_ser; } +size_t Verilated::serialized2Size() VL_PURE { return sizeof(VerilatedImp::s_s.v.m_ser); } +void* Verilated::serialized2Ptr() VL_MT_UNSAFE { return &VerilatedImp::s_s.v.m_ser; } //=========================================================================== // Random -- Mostly called at init time, so not inline. @@ -2422,29 +2427,60 @@ void Verilated::endOfEvalGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { } #endif +//=========================================================================== +// VerilatedImp:: Constructors + +// verilated.o may exist both in protect-lib and main module. +// Both the main module and the protec-lib refer the same instance of +// static variables such as Verilated or VerilatedImplData. +// This is important to share the state such as Verilated::gotFinish. +// But the sharing may cause double-free error when shutting down because destructors +// are called twice. +// 1st time:From protec-lib shared object on the way of unloading after exitting main() +// 2nd time:From main executable. +// +// To avoid the trouble, all member variables are enclosed in VerilatedImpU union. +// ctor nor dtor of members are not called automatically. +// VerilatedImp::setup() and teardown() guarantees to initialize/destruct just once. + +void VerilatedImp::setup() { + static bool done = false; + if (!done) { + new (&VerilatedImp::s_s) VerilatedImpData(); + done = true; + } +} +void VerilatedImp::teardown() { + static bool done = false; + if (!done) { + VerilatedImp::s_s.~VerilatedImpU(); + done = true; + } +} + //=========================================================================== // VerilatedImp:: Methods std::string VerilatedImp::timeFormatSuffix() VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_sergMutex); - return s_s.m_serg.m_timeFormatSuffix; + const VerilatedLockGuard lock(s_s.v.m_sergMutex); + return s_s.v.m_serg.m_timeFormatSuffix; } void VerilatedImp::timeFormatSuffix(const std::string& value) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_sergMutex); - s_s.m_serg.m_timeFormatSuffix = value; + const VerilatedLockGuard lock(s_s.v.m_sergMutex); + s_s.v.m_serg.m_timeFormatSuffix = value; } -void VerilatedImp::timeFormatUnits(int value) VL_MT_SAFE { s_s.m_ser.m_timeFormatUnits = value; } +void VerilatedImp::timeFormatUnits(int value) VL_MT_SAFE { s_s.v.m_ser.m_timeFormatUnits = value; } void VerilatedImp::timeFormatPrecision(int value) VL_MT_SAFE { - s_s.m_ser.m_timeFormatPrecision = value; + s_s.v.m_ser.m_timeFormatPrecision = value; } -void VerilatedImp::timeFormatWidth(int value) VL_MT_SAFE { s_s.m_ser.m_timeFormatWidth = value; } +void VerilatedImp::timeFormatWidth(int value) VL_MT_SAFE { s_s.v.m_ser.m_timeFormatWidth = value; } void VerilatedImp::internalsDump() VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_argMutex); + const VerilatedLockGuard lock(s_s.v.m_argMutex); VL_PRINTF_MT("internalsDump:\n"); versionDump(); VL_PRINTF_MT(" Argv:"); - for (const auto& i : s_s.m_argVec) VL_PRINTF_MT(" %s", i.c_str()); + for (const auto& i : s_s.v.m_argVec) VL_PRINTF_MT(" %s", i.c_str()); VL_PRINTF_MT("\n"); scopesDump(); exportsDump(); @@ -2454,22 +2490,22 @@ void VerilatedImp::versionDump() VL_MT_SAFE { VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion()); } -void VerilatedImp::commandArgs(int argc, const char** argv) VL_EXCLUDES(s_s.m_argMutex) { - const VerilatedLockGuard lock(s_s.m_argMutex); - s_s.m_argVec.clear(); // Always clear +void VerilatedImp::commandArgs(int argc, const char** argv) VL_EXCLUDES(s_s.v.m_argMutex) { + const VerilatedLockGuard lock(s_s.v.m_argMutex); + s_s.v.m_argVec.clear(); // Always clear commandArgsAddGuts(argc, argv); } -void VerilatedImp::commandArgsAdd(int argc, const char** argv) VL_EXCLUDES(s_s.m_argMutex) { - const VerilatedLockGuard lock(s_s.m_argMutex); +void VerilatedImp::commandArgsAdd(int argc, const char** argv) VL_EXCLUDES(s_s.v.m_argMutex) { + const VerilatedLockGuard lock(s_s.v.m_argMutex); commandArgsAddGuts(argc, argv); } -void VerilatedImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.m_argMutex) { - if (!s_s.m_argVecLoaded) s_s.m_argVec.clear(); +void VerilatedImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.v.m_argMutex) { + if (!s_s.v.m_argVecLoaded) s_s.v.m_argVec.clear(); for (int i = 0; i < argc; ++i) { - s_s.m_argVec.push_back(argv[i]); + s_s.v.m_argVec.push_back(argv[i]); commandArgVl(argv[i]); } - s_s.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok + s_s.v.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok } void VerilatedImp::commandArgVl(const std::string& arg) { if (0 == strncmp(arg.c_str(), "+verilator+", strlen("+verilator+"))) { diff --git a/include/verilated_imp.h b/include/verilated_imp.h index c72001534..a84aa13ba 100644 --- a/include/verilated_imp.h +++ b/include/verilated_imp.h @@ -189,10 +189,11 @@ public: //====================================================================== // VerilatedImp -class VerilatedImp { +class VerilatedImpData { // Whole class is internal use only - Global information shared between verilated*.cpp files. protected: friend class Verilated; + friend class VerilatedImp; // TYPES typedef std::vector ArgVec; @@ -200,7 +201,6 @@ protected: typedef std::map ExportNameMap; // MEMBERS - static VerilatedImp s_s; ///< Static Singleton; One and only static this struct Serialized { // All these members serialized/deserialized int m_timeFormatUnits = UNITS_NONE; // $timeformat units @@ -248,19 +248,37 @@ protected: // List of free descriptors in the MCT region [4, 32) std::vector m_fdFreeMct VL_GUARDED_BY(m_fdMutex); -public: // But only for verilated*.cpp // CONSTRUCTORS - VerilatedImp() + VerilatedImpData() : m_argVecLoaded{false} , m_exportNext{0} { - s_s.m_fdps.resize(31); - std::fill(s_s.m_fdps.begin(), s_s.m_fdps.end(), (FILE*)0); - s_s.m_fdFreeMct.resize(30); - for (std::size_t i = 0, id = 1; i < s_s.m_fdFreeMct.size(); ++i, ++id) { - s_s.m_fdFreeMct[i] = id; - } + + m_fdps.resize(31); + std::fill(m_fdps.begin(), m_fdps.end(), (FILE*)0); + m_fdFreeMct.resize(30); + for (std::size_t i = 0, id = 1; i < m_fdFreeMct.size(); ++i, ++id) { m_fdFreeMct[i] = id; } } +}; + +class VerilatedImp { + // Whole class is internal use only - Global information shared between verilated*.cpp files. +protected: + friend class Verilated; + + // MEMBERS + union VerilatedImpU { ///< Enclose in an union to call ctor/dtor manually + VerilatedImpData v; + VerilatedImpU() {} + ~VerilatedImpU() {} + }; + static VerilatedImpU s_s; ///< Static Singleton; One and only static this + +public: // But only for verilated*.cpp + // CONSTRUCTORS + VerilatedImp() {} ~VerilatedImp() {} + static void setup(); + static void teardown(); private: VL_UNCOPYABLE(VerilatedImp); @@ -272,19 +290,19 @@ public: // METHODS - arguments public: - static void commandArgs(int argc, const char** argv) VL_EXCLUDES(s_s.m_argMutex); - static void commandArgsAdd(int argc, const char** argv) VL_EXCLUDES(s_s.m_argMutex); - static std::string argPlusMatch(const char* prefixp) VL_EXCLUDES(s_s.m_argMutex) { - const VerilatedLockGuard lock(s_s.m_argMutex); + static void commandArgs(int argc, const char** argv) VL_EXCLUDES(s_s.v.m_argMutex); + static void commandArgsAdd(int argc, const char** argv) VL_EXCLUDES(s_s.v.m_argMutex); + static std::string argPlusMatch(const char* prefixp) VL_EXCLUDES(s_s.v.m_argMutex) { + const VerilatedLockGuard lock(s_s.v.m_argMutex); // Note prefixp does not include the leading "+" size_t len = strlen(prefixp); - if (VL_UNLIKELY(!s_s.m_argVecLoaded)) { - s_s.m_argVecLoaded = true; // Complain only once + if (VL_UNLIKELY(!s_s.v.m_argVecLoaded)) { + s_s.v.m_argVecLoaded = true; // Complain only once VL_FATAL_MT("unknown", 0, "", "%Error: Verilog called $test$plusargs or $value$plusargs without" " testbench C first calling Verilated::commandArgs(argc,argv)."); } - for (const auto& i : s_s.m_argVec) { + for (const auto& i : s_s.v.m_argVec) { if (i[0] == '+') { if (0 == strncmp(prefixp, i.c_str() + 1, len)) return i; } @@ -293,7 +311,7 @@ public: } private: - static void commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.m_argMutex); + static void commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(s_s.v.m_argMutex); static void commandArgVl(const std::string& arg); static bool commandArgVlValue(const std::string& arg, const std::string& prefix, std::string& valuer); @@ -304,18 +322,18 @@ public: // There's often many more scopes than userdata's and thus having a ~48byte // per map overhead * N scopes would take much more space and cache thrashing. static inline void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_userMapMutex); - const auto it = s_s.m_userMap.find(std::make_pair(scopep, userKey)); - if (it != s_s.m_userMap.end()) { + const VerilatedLockGuard lock(s_s.v.m_userMapMutex); + const auto it = s_s.v.m_userMap.find(std::make_pair(scopep, userKey)); + if (it != s_s.v.m_userMap.end()) { it->second = userData; } else { - s_s.m_userMap.insert(it, std::make_pair(std::make_pair(scopep, userKey), userData)); + s_s.v.m_userMap.insert(it, std::make_pair(std::make_pair(scopep, userKey), userData)); } } static inline void* userFind(const void* scopep, void* userKey) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_userMapMutex); - const auto& it = vlstd::as_const(s_s.m_userMap).find(std::make_pair(scopep, userKey)); - if (VL_UNLIKELY(it == s_s.m_userMap.end())) return nullptr; + const VerilatedLockGuard lock(s_s.v.m_userMapMutex); + const auto& it = vlstd::as_const(s_s.v.m_userMap).find(std::make_pair(scopep, userKey)); + if (VL_UNLIKELY(it == s_s.v.m_userMap.end())) return nullptr; return it->second; } @@ -323,19 +341,20 @@ private: /// Symbol table destruction cleans up the entries for each scope. static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE { // Slow ok - called once/scope on destruction, so we simply iterate. - const VerilatedLockGuard lock(s_s.m_userMapMutex); - for (auto it = s_s.m_userMap.begin(); it != s_s.m_userMap.end();) { + const VerilatedLockGuard lock(s_s.v.m_userMapMutex); + for (auto it = s_s.v.m_userMap.begin(); it != s_s.v.m_userMap.end();) { if (it->first.first == scopep) { - s_s.m_userMap.erase(it++); + s_s.v.m_userMap.erase(it++); } else { ++it; } } } static void userDump() VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_userMapMutex); // Avoid it changing in middle of dump + const VerilatedLockGuard lock( + s_s.v.m_userMapMutex); // Avoid it changing in middle of dump bool first = true; - for (const auto& i : s_s.m_userMap) { + for (const auto& i : s_s.v.m_userMap) { if (first) { VL_PRINTF_MT(" userDump:\n"); first = false; @@ -349,30 +368,30 @@ public: // But only for verilated*.cpp // METHODS - scope name static void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE { // Slow ok - called once/scope at construction - const VerilatedLockGuard lock(s_s.m_nameMutex); - const auto it = s_s.m_nameMap.find(scopep->name()); - if (it == s_s.m_nameMap.end()) { - s_s.m_nameMap.insert(it, std::make_pair(scopep->name(), scopep)); + const VerilatedLockGuard lock(s_s.v.m_nameMutex); + const auto it = s_s.v.m_nameMap.find(scopep->name()); + if (it == s_s.v.m_nameMap.end()) { + s_s.v.m_nameMap.insert(it, std::make_pair(scopep->name(), scopep)); } } static inline const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_nameMutex); + const VerilatedLockGuard lock(s_s.v.m_nameMutex); // If too slow, can assume this is only VL_MT_SAFE_POSINIT - const auto& it = s_s.m_nameMap.find(namep); - if (VL_UNLIKELY(it == s_s.m_nameMap.end())) return nullptr; + const auto& it = s_s.v.m_nameMap.find(namep); + if (VL_UNLIKELY(it == s_s.v.m_nameMap.end())) return nullptr; return it->second; } static void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE { // Slow ok - called once/scope at destruction - const VerilatedLockGuard lock(s_s.m_nameMutex); + const VerilatedLockGuard lock(s_s.v.m_nameMutex); userEraseScope(scopep); - const auto it = s_s.m_nameMap.find(scopep->name()); - if (it != s_s.m_nameMap.end()) s_s.m_nameMap.erase(it); + const auto it = s_s.v.m_nameMap.find(scopep->name()); + if (it != s_s.v.m_nameMap.end()) s_s.v.m_nameMap.erase(it); } static void scopesDump() VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_nameMutex); + const VerilatedLockGuard lock(s_s.v.m_nameMutex); VL_PRINTF_MT(" scopesDump:\n"); - for (const auto& i : s_s.m_nameMap) { + for (const auto& i : s_s.v.m_nameMap) { const VerilatedScope* scopep = i.second; scopep->scopeDump(); } @@ -380,19 +399,19 @@ public: // But only for verilated*.cpp } static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE_POSTINIT { // Thread save only assuming this is called only after model construction completed - return &s_s.m_nameMap; + return &s_s.v.m_nameMap; } public: // But only for verilated*.cpp // METHODS - hierarchy static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE { // Slow ok - called at construction for VPI accessible elements - const VerilatedLockGuard lock(s_s.m_hierMapMutex); - s_s.m_hierMap[fromp].push_back(top); + const VerilatedLockGuard lock(s_s.v.m_hierMapMutex); + s_s.v.m_hierMap[fromp].push_back(top); } static const VerilatedHierarchyMap* hierarchyMap() VL_MT_SAFE_POSTINIT { // Thread save only assuming this is called only after model construction completed - return &s_s.m_hierMap; + return &s_s.v.m_hierMap; } public: // But only for verilated*.cpp @@ -406,19 +425,19 @@ public: // But only for verilated*.cpp // miss at the cost of a multiply, and all lookups move to slowpath. static int exportInsert(const char* namep) VL_MT_SAFE { // Slow ok - called once/function at creation - const VerilatedLockGuard lock(s_s.m_exportMutex); - const auto it = s_s.m_exportMap.find(namep); - if (it == s_s.m_exportMap.end()) { - s_s.m_exportMap.insert(it, std::make_pair(namep, s_s.m_exportNext++)); - return s_s.m_exportNext++; + const VerilatedLockGuard lock(s_s.v.m_exportMutex); + const auto it = s_s.v.m_exportMap.find(namep); + if (it == s_s.v.m_exportMap.end()) { + s_s.v.m_exportMap.insert(it, std::make_pair(namep, s_s.v.m_exportNext++)); + return s_s.v.m_exportNext++; } else { return it->second; } } static int exportFind(const char* namep) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_exportMutex); - const auto& it = s_s.m_exportMap.find(namep); - if (VL_LIKELY(it != s_s.m_exportMap.end())) return it->second; + const VerilatedLockGuard lock(s_s.v.m_exportMutex); + const auto& it = s_s.v.m_exportMap.find(namep); + if (VL_LIKELY(it != s_s.v.m_exportMap.end())) return it->second; std::string msg = (std::string("%Error: Testbench C called ") + namep + " but no such DPI export function name exists in ANY model"); VL_FATAL_MT("unknown", 0, "", msg.c_str()); @@ -426,16 +445,16 @@ public: // But only for verilated*.cpp } static const char* exportName(int funcnum) VL_MT_SAFE { // Slowpath; find name for given export; errors only so no map to reverse-map it - const VerilatedLockGuard lock(s_s.m_exportMutex); - for (const auto& i : s_s.m_exportMap) { + const VerilatedLockGuard lock(s_s.v.m_exportMutex); + for (const auto& i : s_s.v.m_exportMap) { if (i.second == funcnum) return i.first; } return "*UNKNOWN*"; } static void exportsDump() VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_exportMutex); + const VerilatedLockGuard lock(s_s.v.m_exportMutex); bool first = true; - for (const auto& i : s_s.m_exportMap) { + for (const auto& i : s_s.v.m_exportMap) { if (first) { VL_PRINTF_MT(" exportDump:\n"); first = false; @@ -451,13 +470,13 @@ public: // But only for verilated*.cpp static std::string timeFormatSuffix() VL_MT_SAFE; static void timeFormatSuffix(const std::string& value) VL_MT_SAFE; static int timeFormatUnits() VL_MT_SAFE { - if (s_s.m_ser.m_timeFormatUnits == Serialized::UNITS_NONE) { + if (s_s.v.m_ser.m_timeFormatUnits == VerilatedImpData::Serialized::UNITS_NONE) { return Verilated::timeprecision(); } - return s_s.m_ser.m_timeFormatUnits; + return s_s.v.m_ser.m_timeFormatUnits; } - static int timeFormatPrecision() VL_MT_SAFE { return s_s.m_ser.m_timeFormatPrecision; } - static int timeFormatWidth() VL_MT_SAFE { return s_s.m_ser.m_timeFormatWidth; } + static int timeFormatPrecision() VL_MT_SAFE { return s_s.v.m_ser.m_timeFormatPrecision; } + static int timeFormatWidth() VL_MT_SAFE { return s_s.v.m_ser.m_timeFormatWidth; } static void timeFormatUnits(int value) VL_MT_SAFE; static void timeFormatPrecision(int value) VL_MT_SAFE; static void timeFormatWidth(int value) VL_MT_SAFE; @@ -465,55 +484,55 @@ public: // But only for verilated*.cpp public: // But only for verilated*.cpp // METHODS - file IO static IData fdNewMcd(const char* filenamep) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_fdMutex); - if (s_s.m_fdFreeMct.empty()) return 0; - IData idx = s_s.m_fdFreeMct.back(); - s_s.m_fdFreeMct.pop_back(); - s_s.m_fdps[idx] = fopen(filenamep, "w"); - if (VL_UNLIKELY(!s_s.m_fdps[idx])) return 0; + const VerilatedLockGuard lock(s_s.v.m_fdMutex); + if (s_s.v.m_fdFreeMct.empty()) return 0; + IData idx = s_s.v.m_fdFreeMct.back(); + s_s.v.m_fdFreeMct.pop_back(); + s_s.v.m_fdps[idx] = fopen(filenamep, "w"); + if (VL_UNLIKELY(!s_s.v.m_fdps[idx])) return 0; return (1 << idx); } static IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE { FILE* fp = fopen(filenamep, modep); if (VL_UNLIKELY(!fp)) return 0; // Bit 31 indicates it's a descriptor not a MCD - const VerilatedLockGuard lock(s_s.m_fdMutex); - if (s_s.m_fdFree.empty()) { + const VerilatedLockGuard lock(s_s.v.m_fdMutex); + if (s_s.v.m_fdFree.empty()) { // Need to create more space in m_fdps and m_fdFree - const std::size_t start = std::max(31UL + 1UL + 3UL, s_s.m_fdps.size()); + const std::size_t start = std::max(31UL + 1UL + 3UL, s_s.v.m_fdps.size()); const std::size_t excess = 10; - s_s.m_fdps.resize(start + excess); - std::fill(s_s.m_fdps.begin() + start, s_s.m_fdps.end(), (FILE*)0); - s_s.m_fdFree.resize(excess); - for (std::size_t i = 0, id = start; i < s_s.m_fdFree.size(); ++i, ++id) { - s_s.m_fdFree[i] = id; + s_s.v.m_fdps.resize(start + excess); + std::fill(s_s.v.m_fdps.begin() + start, s_s.v.m_fdps.end(), (FILE*)0); + s_s.v.m_fdFree.resize(excess); + for (std::size_t i = 0, id = start; i < s_s.v.m_fdFree.size(); ++i, ++id) { + s_s.v.m_fdFree[i] = id; } } - IData idx = s_s.m_fdFree.back(); - s_s.m_fdFree.pop_back(); - s_s.m_fdps[idx] = fp; + IData idx = s_s.v.m_fdFree.back(); + s_s.v.m_fdFree.pop_back(); + s_s.v.m_fdps[idx] = fp; return (idx | (1UL << 31)); // bit 31 indicates not MCD } static void fdFlush(IData fdi) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_fdMutex); + const VerilatedLockGuard lock(s_s.v.m_fdMutex); const VerilatedFpList fdlist = fdToFpList(fdi); for (const auto& i : fdlist) fflush(i); } static IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_fdMutex); + const VerilatedLockGuard lock(s_s.v.m_fdMutex); const VerilatedFpList fdlist = fdToFpList(fdi); if (VL_UNLIKELY(fdlist.size() != 1)) return 0; return static_cast( fseek(*fdlist.begin(), static_cast(offset), static_cast(origin))); } static IData fdTell(IData fdi) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_fdMutex); + const VerilatedLockGuard lock(s_s.v.m_fdMutex); const VerilatedFpList fdlist = fdToFpList(fdi); if (VL_UNLIKELY(fdlist.size() != 1)) return 0; return static_cast(ftell(*fdlist.begin())); } static void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_fdMutex); + const VerilatedLockGuard lock(s_s.v.m_fdMutex); const VerilatedFpList fdlist = fdToFpList(fdi); for (const auto& i : fdlist) { if (VL_UNLIKELY(!i)) continue; @@ -521,35 +540,35 @@ public: // But only for verilated*.cpp } } static void fdClose(IData fdi) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_fdMutex); + const VerilatedLockGuard lock(s_s.v.m_fdMutex); if ((fdi & (1 << 31)) != 0) { // Non-MCD case IData idx = VL_MASK_I(31) & fdi; - if (VL_UNLIKELY(idx >= s_s.m_fdps.size())) return; - if (VL_UNLIKELY(!s_s.m_fdps[idx])) return; // Already free - fclose(s_s.m_fdps[idx]); - s_s.m_fdps[idx] = (FILE*)0; - s_s.m_fdFree.push_back(idx); + if (VL_UNLIKELY(idx >= s_s.v.m_fdps.size())) return; + if (VL_UNLIKELY(!s_s.v.m_fdps[idx])) return; // Already free + fclose(s_s.v.m_fdps[idx]); + s_s.v.m_fdps[idx] = (FILE*)0; + s_s.v.m_fdFree.push_back(idx); } else { // MCD case for (int i = 0; (fdi != 0) && (i < 31); i++, fdi >>= 1) { if (fdi & VL_MASK_I(1)) { - fclose(s_s.m_fdps[i]); - s_s.m_fdps[i] = nullptr; - s_s.m_fdFreeMct.push_back(i); + fclose(s_s.v.m_fdps[i]); + s_s.v.m_fdps[i] = nullptr; + s_s.v.m_fdFreeMct.push_back(i); } } } } static inline FILE* fdToFp(IData fdi) VL_MT_SAFE { - const VerilatedLockGuard lock(s_s.m_fdMutex); + const VerilatedLockGuard lock(s_s.v.m_fdMutex); const VerilatedFpList fdlist = fdToFpList(fdi); if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr; return *fdlist.begin(); } private: - static inline VerilatedFpList fdToFpList(IData fdi) VL_REQUIRES(s_s.m_fdMutex) { + static inline VerilatedFpList fdToFpList(IData fdi) VL_REQUIRES(s_s.v.m_fdMutex) { VerilatedFpList fp; if ((fdi & (1 << 31)) != 0) { // Non-MCD case @@ -559,13 +578,13 @@ private: case 1: fp.push_back(stdout); break; case 2: fp.push_back(stderr); break; default: - if (VL_LIKELY(idx < s_s.m_fdps.size())) fp.push_back(s_s.m_fdps[idx]); + if (VL_LIKELY(idx < s_s.v.m_fdps.size())) fp.push_back(s_s.v.m_fdps[idx]); break; } } else { // MCD Case for (int i = 0; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) { - if (fdi & VL_MASK_I(1)) fp.push_back(s_s.m_fdps[i]); + if (fdi & VL_MASK_I(1)) fp.push_back(s_s.v.m_fdps[i]); } } return fp; diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index 0acfb4942..e0cfd151a 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -261,7 +261,7 @@ public: // So add dynamic_lookup of.puts("ifeq ($(shell uname -s),Darwin)\n"); of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -undefined " - "dynamic_lookup -shared -o $@ $^\n"); + "dynamic_lookup -shared -flat_namespace -o $@ $^\n"); of.puts("else\n"); of.puts( "\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -shared -o $@ $^\n"); @@ -331,8 +331,11 @@ class EmitMkHierVerilation { of.puts("# Libraries of hierarchical blocks\n"); of.puts("VM_HIER_LIBS := \\\n"); - for (V3HierBlockPlan::const_iterator it = m_planp->begin(); it != m_planp->end(); ++it) { - of.puts("\t" + it->second->hierLib(true) + " \\\n"); + const V3HierBlockPlan::HierVector blocks + = m_planp->hierBlocksSorted(); // leaf comes first + // List in order of leaf-last order so that linker can resolve dependency + for (auto it = blocks.rbegin(); it != blocks.rend(); ++it) { + of.puts("\t" + (*it)->hierLib(true) + " \\\n"); } of.puts("\n"); diff --git a/test_regress/t/t_hier_block_prot_lib_shared.pl b/test_regress/t/t_hier_block_prot_lib_shared.pl new file mode 100755 index 000000000..0abe249aa --- /dev/null +++ b/test_regress/t/t_hier_block_prot_lib_shared.pl @@ -0,0 +1,67 @@ +#!/usr/bin/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. + +top_filename("t/t_hier_block.v"); + +scenarios(vlt_all => 1, xsim => 1); + +my $secret_prefix = "secret"; +my $secret_dir = "$Self->{obj_dir}/$secret_prefix"; +mkdir $secret_dir; +my $abs_secret_dir = File::Spec->rel2abs($secret_dir); + +while (1) { + # Always compile the secret file with Verilator no matter what simulator + # we are testing with + run(logfile => "$secret_dir/vlt_compile.log", + cmd => ["perl", + "$ENV{VERILATOR_ROOT}/bin/verilator", + "-cc", + "--hierarchical", + "-Mdir", + $secret_dir, + "--protect-lib", + $secret_prefix, + "--protect-key", + "PROTECT_KEY", + "t/t_hier_block.v", + "-DAS_PROT_LIB", + '--CFLAGS', '"-pipe -DCPP_MACRO=cplusplus"', + $Self->{vltmt} ? ' --threads 1' : '', + "--build"], + verilator_run => 1, + ); + last if $Self->{errors}; + + compile( + v_flags2 => ['t/t_hier_block.cpp'], + verilator_flags2 => ["$secret_dir/secret.sv", + "-DPROTLIB_TOP", + "--top-module t", + "-LDFLAGS", + "'-Wl,-rpath,$abs_secret_dir -L$abs_secret_dir -l$secret_prefix'"], + ); + + execute( + check_finished => 1, + run_env => "DYLD_FALLBACK_LIBRARY_PATH=$abs_secret_dir" + ); + + + ok(1); + last; +} + +file_grep($secret_dir . "/Vsub0/sub0.sv", /^module\s+(\S+)\s+/, "sub0"); +file_grep($secret_dir . "/Vsub1/sub1.sv", /^module\s+(\S+)\s+/, "sub1"); +file_grep($secret_dir . "/Vsub2/sub2.sv", /^module\s+(\S+)\s+/, "sub2"); +file_grep($Self->{run_log_filename}, qr/MACRO:(\S+) is defined/i, "cplusplus"); + +ok(1); +1; diff --git a/test_regress/t/t_prot_lib_shared.pl b/test_regress/t/t_prot_lib_shared.pl new file mode 100755 index 000000000..a595a4866 --- /dev/null +++ b/test_regress/t/t_prot_lib_shared.pl @@ -0,0 +1,81 @@ +#!/usr/bin/env perl +# Makes the test run with tracing enabled by default, can be overridden +# with --notrace +unshift(@ARGV, "--trace"); +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2019 by Todd Strader. 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( + vlt => 1, + vltmt => 1, + xsim => 1, + ); +top_filename("t/t_prot_lib.v"); + +$Self->{sim_time} = $Self->{benchmark} * 100 if $Self->{benchmark}; + +my $secret_prefix = "secret"; +my $secret_dir = "$Self->{obj_dir}/$secret_prefix"; +my $abs_secret_dir = File::Spec->rel2abs($secret_dir); +mkdir $secret_dir; + +while (1) { + # Always compile the secret file with Verilator no matter what simulator + # we are testing with + run(logfile => "$secret_dir/vlt_compile.log", + cmd => ["perl", + "$ENV{VERILATOR_ROOT}/bin/verilator", + ($Self->{vltmt} ? ' --threads 6' : ''), + "--prefix", + "Vt_prot_lib_secret", + "-cc", + "-Mdir", + $secret_dir, + "--protect-lib", + $secret_prefix, + "t/t_prot_lib_secret.v"], + verilator_run => 1, + ); + last if $Self->{errors}; + + run(logfile => "$secret_dir/secret_gcc.log", + cmd=>[$ENV{MAKE}, + "-C", + $secret_dir, + "-f", + "Vt_prot_lib_secret.mk"]); + last if $Self->{errors}; + + compile( + verilator_flags2 => ["$secret_dir/secret.sv", + ($Self->{vltmt} ? ' --threads 1' : ''), + "-LDFLAGS", + "'-Wl,-rpath,$abs_secret_dir -L$abs_secret_dir -l$secret_prefix'"], + xsim_flags2 => ["$secret_dir/secret.sv"], + ); + + execute( + check_finished => 1, + run_env => "DYLD_FALLBACK_LIBRARY_PATH=$abs_secret_dir", + xsim_run_flags2 => ["--sv_lib", + "$secret_dir/libsecret", + "--dpi_absolute"], + ); + + if ($Self->{vlt} && $Self->{trace}) { + # We can see the ports of the secret module + file_grep("$Self->{obj_dir}/simx.vcd", qr/accum_in/); + # but we can't see what's inside + file_grep_not("$Self->{obj_dir}/simx.vcd", qr/secret_/); + } + + ok(1); + last; +} +1;