Fix double-free on shared protect-lib (#2526)

* Add a test to use shared object of protect-lib
* Add a guard to call ctor/dtor just once even when a protec-lib is shared object.
* Pass .a to linker in leaf-last order for older ld.
* Add -flat_namespace for mac
This commit is contained in:
Yutetsu TAKATSUKASA 2020-08-31 21:22:31 +09:00 committed by GitHub
parent dc1ad16b03
commit 70eb99b050
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 326 additions and 120 deletions

View File

@ -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+"))) {

View File

@ -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<std::string> ArgVec;
@ -200,7 +201,6 @@ protected:
typedef std::map<const char*, int, VerilatedCStrCmp> 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<IData> 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<std::size_t>(31UL + 1UL + 3UL, s_s.m_fdps.size());
const std::size_t start = std::max<std::size_t>(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<IData>(
fseek(*fdlist.begin(), static_cast<long>(offset), static_cast<int>(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<IData>(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;

View File

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

View File

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

View File

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