Optimize trace code for faster compiles on repeated types (#6707)
This commit is contained in:
parent
394d9cf168
commit
35dcf70f48
|
|
@ -1035,6 +1035,16 @@ public:
|
||||||
bool isWritable() const VL_MT_SAFE { return m_e == OUTPUT || m_e == INOUT || m_e == REF; }
|
bool isWritable() const VL_MT_SAFE { return m_e == OUTPUT || m_e == INOUT || m_e == REF; }
|
||||||
bool isRef() const VL_MT_SAFE { return m_e == REF; }
|
bool isRef() const VL_MT_SAFE { return m_e == REF; }
|
||||||
bool isConstRef() const VL_MT_SAFE { return m_e == CONSTREF; }
|
bool isConstRef() const VL_MT_SAFE { return m_e == CONSTREF; }
|
||||||
|
string traceSigDirection() const {
|
||||||
|
if (isInout()) {
|
||||||
|
return "VerilatedTraceSigDirection::INOUT";
|
||||||
|
} else if (isWritable()) {
|
||||||
|
return "VerilatedTraceSigDirection::OUTPUT";
|
||||||
|
} else if (isNonOutput()) {
|
||||||
|
return "VerilatedTraceSigDirection::INPUT";
|
||||||
|
}
|
||||||
|
return "VerilatedTraceSigDirection::NONE";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
constexpr bool operator==(const VDirection& lhs, const VDirection& rhs) VL_MT_SAFE {
|
constexpr bool operator==(const VDirection& lhs, const VDirection& rhs) VL_MT_SAFE {
|
||||||
return lhs.m_e == rhs.m_e;
|
return lhs.m_e == rhs.m_e;
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ public:
|
||||||
// Ideally an IEEE $typename
|
// Ideally an IEEE $typename
|
||||||
virtual string prettyDTypeName(bool) const { return prettyTypeName(); }
|
virtual string prettyDTypeName(bool) const { return prettyTypeName(); }
|
||||||
string prettyDTypeNameQ() const { return "'" + prettyDTypeName(false) + "'"; }
|
string prettyDTypeNameQ() const { return "'" + prettyDTypeName(false) + "'"; }
|
||||||
|
virtual string cDTypeName() const { return ""; }
|
||||||
//
|
//
|
||||||
// Changing the width may confuse the data type resolution, so must clear
|
// Changing the width may confuse the data type resolution, so must clear
|
||||||
// TypeTable cache after use.
|
// TypeTable cache after use.
|
||||||
|
|
@ -220,7 +221,19 @@ public:
|
||||||
return elementsConst() * subDTypep()->widthTotalBytes();
|
return elementsConst() * subDTypep()->widthTotalBytes();
|
||||||
}
|
}
|
||||||
inline int left() const VL_MT_STABLE;
|
inline int left() const VL_MT_STABLE;
|
||||||
|
string cLeft() const {
|
||||||
|
std::ostringstream os;
|
||||||
|
if (left() < 0) os << "__02D";
|
||||||
|
os << abs(left());
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
inline int right() const VL_MT_STABLE;
|
inline int right() const VL_MT_STABLE;
|
||||||
|
string cRight() const {
|
||||||
|
std::ostringstream os;
|
||||||
|
if (right() < 0) os << "__02D";
|
||||||
|
os << abs(right());
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
inline int hi() const VL_MT_STABLE;
|
inline int hi() const VL_MT_STABLE;
|
||||||
inline int lo() const VL_MT_STABLE;
|
inline int lo() const VL_MT_STABLE;
|
||||||
inline int elementsConst() const VL_MT_STABLE;
|
inline int elementsConst() const VL_MT_STABLE;
|
||||||
|
|
@ -259,6 +272,7 @@ public:
|
||||||
void dump(std::ostream& str) const override;
|
void dump(std::ostream& str) const override;
|
||||||
void dumpJson(std::ostream& str) const override;
|
void dumpJson(std::ostream& str) const override;
|
||||||
string prettyDTypeName(bool) const override;
|
string prettyDTypeName(bool) const override;
|
||||||
|
string cDTypeName() const override;
|
||||||
bool isCompound() const override { return !packed(); }
|
bool isCompound() const override { return !packed(); }
|
||||||
// For basicp() we reuse the size to indicate a "fake" basic type of same size
|
// For basicp() we reuse the size to indicate a "fake" basic type of same size
|
||||||
AstBasicDType* basicp() const override VL_MT_STABLE {
|
AstBasicDType* basicp() const override VL_MT_STABLE {
|
||||||
|
|
@ -433,6 +447,7 @@ public:
|
||||||
bool similarDTypeNode(const AstNodeDType* samep) const override;
|
bool similarDTypeNode(const AstNodeDType* samep) const override;
|
||||||
string name() const override VL_MT_STABLE { return m.m_keyword.ascii(); }
|
string name() const override VL_MT_STABLE { return m.m_keyword.ascii(); }
|
||||||
string prettyDTypeName(bool full) const override;
|
string prettyDTypeName(bool full) const override;
|
||||||
|
string cDTypeName() const override;
|
||||||
const char* broken() const override {
|
const char* broken() const override {
|
||||||
BROKEN_RTN(dtypep() != this);
|
BROKEN_RTN(dtypep() != this);
|
||||||
BROKEN_RTN(v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH
|
BROKEN_RTN(v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH
|
||||||
|
|
@ -496,7 +511,19 @@ public:
|
||||||
inline int lo() const;
|
inline int lo() const;
|
||||||
inline int elements() const;
|
inline int elements() const;
|
||||||
int left() const { return ascending() ? lo() : hi(); } // How to show a declaration
|
int left() const { return ascending() ? lo() : hi(); } // How to show a declaration
|
||||||
|
string cLeft() const {
|
||||||
|
std::ostringstream os;
|
||||||
|
if (left() < 0) os << "__02D";
|
||||||
|
os << abs(left());
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
int right() const { return ascending() ? hi() : lo(); }
|
int right() const { return ascending() ? hi() : lo(); }
|
||||||
|
string cRight() const {
|
||||||
|
std::ostringstream os;
|
||||||
|
if (right() < 0) os << "__02D";
|
||||||
|
os << abs(right());
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
inline bool ascending() const;
|
inline bool ascending() const;
|
||||||
bool implicit() const { return keyword() == VBasicDTypeKwd::LOGIC_IMPLICIT; }
|
bool implicit() const { return keyword() == VBasicDTypeKwd::LOGIC_IMPLICIT; }
|
||||||
bool untyped() const { return keyword() == VBasicDTypeKwd::UNTYPED; }
|
bool untyped() const { return keyword() == VBasicDTypeKwd::UNTYPED; }
|
||||||
|
|
@ -1210,6 +1237,9 @@ public:
|
||||||
string prettyDTypeName(bool full) const override {
|
string prettyDTypeName(bool full) const override {
|
||||||
return subDTypep() ? prettyName(subDTypep()->prettyDTypeName(full)) : prettyName();
|
return subDTypep() ? prettyName(subDTypep()->prettyDTypeName(full)) : prettyName();
|
||||||
}
|
}
|
||||||
|
string cDTypeName() const override {
|
||||||
|
return subDTypep() ? subDTypep()->cDTypeName() : cDTypeName();
|
||||||
|
}
|
||||||
AstBasicDType* basicp() const override VL_MT_STABLE {
|
AstBasicDType* basicp() const override VL_MT_STABLE {
|
||||||
return subDTypep() ? subDTypep()->basicp() : nullptr;
|
return subDTypep() ? subDTypep()->basicp() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -1421,6 +1451,7 @@ public:
|
||||||
inline AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep);
|
inline AstPackArrayDType(FileLine* fl, AstNodeDType* dtp, AstRange* rangep);
|
||||||
ASTGEN_MEMBERS_AstPackArrayDType;
|
ASTGEN_MEMBERS_AstPackArrayDType;
|
||||||
string prettyDTypeName(bool full) const override;
|
string prettyDTypeName(bool full) const override;
|
||||||
|
string cDTypeName() const override;
|
||||||
bool isCompound() const override { return false; }
|
bool isCompound() const override { return false; }
|
||||||
};
|
};
|
||||||
class AstUnpackArrayDType final : public AstNodeArrayDType {
|
class AstUnpackArrayDType final : public AstNodeArrayDType {
|
||||||
|
|
@ -1448,6 +1479,7 @@ public:
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_AstUnpackArrayDType;
|
ASTGEN_MEMBERS_AstUnpackArrayDType;
|
||||||
string prettyDTypeName(bool full) const override;
|
string prettyDTypeName(bool full) const override;
|
||||||
|
string cDTypeName() const override;
|
||||||
bool sameNode(const AstNode* samep) const override {
|
bool sameNode(const AstNode* samep) const override {
|
||||||
const AstUnpackArrayDType* const sp = VN_DBG_AS(samep, UnpackArrayDType);
|
const AstUnpackArrayDType* const sp = VN_DBG_AS(samep, UnpackArrayDType);
|
||||||
return m_isCompound == sp->m_isCompound;
|
return m_isCompound == sp->m_isCompound;
|
||||||
|
|
|
||||||
|
|
@ -4344,6 +4344,7 @@ public:
|
||||||
string selfPointerProtect(bool useSelfForThis) const {
|
string selfPointerProtect(bool useSelfForThis) const {
|
||||||
return selfPointer().protect(useSelfForThis, protect());
|
return selfPointer().protect(useSelfForThis, protect());
|
||||||
}
|
}
|
||||||
|
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||||
};
|
};
|
||||||
class AstCMethodCall final : public AstNodeCCall {
|
class AstCMethodCall final : public AstNodeCCall {
|
||||||
// C++ method call
|
// C++ method call
|
||||||
|
|
|
||||||
|
|
@ -2048,7 +2048,7 @@ public:
|
||||||
string dpiTmpVarType(const string& varName) const;
|
string dpiTmpVarType(const string& varName) const;
|
||||||
// Return Verilator internal type for argument: CData, SData, IData, WData
|
// Return Verilator internal type for argument: CData, SData, IData, WData
|
||||||
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
|
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
|
||||||
bool asRef = false) const;
|
bool asRef = false, bool constRef = false) const;
|
||||||
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
|
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
|
||||||
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
|
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
|
||||||
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
|
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
|
||||||
|
|
@ -2262,6 +2262,8 @@ class AstVarScope final : public AstNode {
|
||||||
// @astgen ptr := m_varp : Optional[AstVar] // [AfterLink] Pointer to variable itself
|
// @astgen ptr := m_varp : Optional[AstVar] // [AfterLink] Pointer to variable itself
|
||||||
bool m_trace : 1; // Tracing is turned on for this scope
|
bool m_trace : 1; // Tracing is turned on for this scope
|
||||||
bool m_optimizeLifePost : 1; // One half of an NBA pair using ShadowVariable scheme. Optimize.
|
bool m_optimizeLifePost : 1; // One half of an NBA pair using ShadowVariable scheme. Optimize.
|
||||||
|
// NOCOMMIT -- is this the right way?
|
||||||
|
bool m_tracePreserve : 1; // Preserve for trace logic
|
||||||
public:
|
public:
|
||||||
AstVarScope(FileLine* fl, AstScope* scopep, AstVar* varp)
|
AstVarScope(FileLine* fl, AstScope* scopep, AstVar* varp)
|
||||||
: ASTGEN_SUPER_VarScope(fl)
|
: ASTGEN_SUPER_VarScope(fl)
|
||||||
|
|
@ -2271,6 +2273,7 @@ public:
|
||||||
UASSERT_OBJ(varp, fl, "Var must be non-null");
|
UASSERT_OBJ(varp, fl, "Var must be non-null");
|
||||||
m_trace = true;
|
m_trace = true;
|
||||||
m_optimizeLifePost = false;
|
m_optimizeLifePost = false;
|
||||||
|
m_tracePreserve = false;
|
||||||
dtypeFrom(varp);
|
dtypeFrom(varp);
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_AstVarScope;
|
ASTGEN_MEMBERS_AstVarScope;
|
||||||
|
|
@ -2293,6 +2296,8 @@ public:
|
||||||
void trace(bool flag) { m_trace = flag; }
|
void trace(bool flag) { m_trace = flag; }
|
||||||
bool optimizeLifePost() const { return m_optimizeLifePost; }
|
bool optimizeLifePost() const { return m_optimizeLifePost; }
|
||||||
void optimizeLifePost(bool flag) { m_optimizeLifePost = flag; }
|
void optimizeLifePost(bool flag) { m_optimizeLifePost = flag; }
|
||||||
|
bool tracePreserve() const { return m_tracePreserve; }
|
||||||
|
void tracePreserve(bool tracePreserve) { m_tracePreserve = tracePreserve; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// === AstNodeCoverDecl ===
|
// === AstNodeCoverDecl ===
|
||||||
|
|
|
||||||
|
|
@ -1196,6 +1196,9 @@ class AstTraceDecl final : public AstNodeStmt {
|
||||||
// Parents: {statement list}
|
// Parents: {statement list}
|
||||||
// Expression being traced - Moved to AstTraceInc by V3Trace
|
// Expression being traced - Moved to AstTraceInc by V3Trace
|
||||||
// @astgen op1 := valuep : Optional[AstNodeExpr]
|
// @astgen op1 := valuep : Optional[AstNodeExpr]
|
||||||
|
//
|
||||||
|
// @astgen ptr := m_dtypeVscp: Optional[AstVarScope] // Var scope for type tracing
|
||||||
|
// @astgen ptr := m_dtypeCallp: Optional[AstCCall] // Type init function call
|
||||||
uint32_t m_code{0}; // Trace identifier code
|
uint32_t m_code{0}; // Trace identifier code
|
||||||
uint32_t m_fidx{0}; // Trace function index
|
uint32_t m_fidx{0}; // Trace function index
|
||||||
const string m_showname; // Name of variable
|
const string m_showname; // Name of variable
|
||||||
|
|
@ -1203,18 +1206,26 @@ class AstTraceDecl final : public AstNodeStmt {
|
||||||
const VNumRange m_arrayRange; // Property of var the trace details
|
const VNumRange m_arrayRange; // Property of var the trace details
|
||||||
const VVarType m_varType; // Type of variable (for localparam vs. param)
|
const VVarType m_varType; // Type of variable (for localparam vs. param)
|
||||||
const VDirection m_declDirection; // Declared direction input/output etc
|
const VDirection m_declDirection; // Declared direction input/output etc
|
||||||
|
// NOCOMMIT -- pretty sure something isn't needed here
|
||||||
|
const bool m_inDtypeFunc; // Trace decl inside type init function
|
||||||
|
string m_dtypeParamName; // Parameter name for type functions
|
||||||
|
int m_codeInc{0}; // Code increment for type
|
||||||
public:
|
public:
|
||||||
AstTraceDecl(FileLine* fl, const string& showname,
|
AstTraceDecl(FileLine* fl, const string& showname,
|
||||||
AstVar* varp, // For input/output state etc
|
AstVar* varp, // For input/output state etc
|
||||||
AstNodeExpr* valuep, const VNumRange& bitRange, const VNumRange& arrayRange)
|
AstNodeExpr* valuep, const VNumRange& bitRange, const VNumRange& arrayRange,
|
||||||
|
AstCCall* const dtypeCallp, AstVarScope* const dtypeVscp, const bool inDtypeFunc)
|
||||||
: ASTGEN_SUPER_TraceDecl(fl)
|
: ASTGEN_SUPER_TraceDecl(fl)
|
||||||
, m_showname{showname}
|
, m_showname{showname}
|
||||||
, m_bitRange{bitRange}
|
, m_bitRange{bitRange}
|
||||||
, m_arrayRange{arrayRange}
|
, m_arrayRange{arrayRange}
|
||||||
, m_varType{varp->varType()}
|
, m_varType{varp->varType()}
|
||||||
, m_declDirection{varp->declDirection()} {
|
, m_declDirection{varp->declDirection()}
|
||||||
|
, m_inDtypeFunc{inDtypeFunc} {
|
||||||
dtypeFrom(valuep);
|
dtypeFrom(valuep);
|
||||||
this->valuep(valuep);
|
this->valuep(valuep);
|
||||||
|
this->dtypeCallp(dtypeCallp);
|
||||||
|
this->dtypeVscp(dtypeVscp);
|
||||||
}
|
}
|
||||||
void dump(std::ostream& str) const override;
|
void dump(std::ostream& str) const override;
|
||||||
void dumpJson(std::ostream& str) const override;
|
void dumpJson(std::ostream& str) const override;
|
||||||
|
|
@ -1223,14 +1234,16 @@ public:
|
||||||
string name() const override VL_MT_STABLE { return m_showname; }
|
string name() const override VL_MT_STABLE { return m_showname; }
|
||||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||||
bool hasDType() const override VL_MT_SAFE { return true; }
|
bool hasDType() const override VL_MT_SAFE { return true; }
|
||||||
bool sameNode(const AstNode* samep) const override { return false; }
|
bool sameNode(const AstNode* samep) const override { return true; }
|
||||||
string showname() const { return m_showname; } // * = Var name
|
string showname() const { return m_showname; } // * = Var name
|
||||||
// Details on what we're tracing
|
// Details on what we're tracing
|
||||||
uint32_t code() const { return m_code; }
|
uint32_t code() const { return m_code; }
|
||||||
void code(uint32_t code) { m_code = code; }
|
void code(uint32_t code) { m_code = code; }
|
||||||
uint32_t fidx() const { return m_fidx; }
|
uint32_t fidx() const { return m_fidx; }
|
||||||
void fidx(uint32_t fidx) { m_fidx = fidx; }
|
void fidx(uint32_t fidx) { m_fidx = fidx; }
|
||||||
|
void codeInc(uint32_t codeInc) { m_codeInc = codeInc; }
|
||||||
uint32_t codeInc() const {
|
uint32_t codeInc() const {
|
||||||
|
if (m_codeInc) { return m_codeInc; }
|
||||||
return (m_arrayRange.ranged() ? m_arrayRange.elements() : 1)
|
return (m_arrayRange.ranged() ? m_arrayRange.elements() : 1)
|
||||||
* valuep()->dtypep()->widthWords()
|
* valuep()->dtypep()->widthWords()
|
||||||
* (VL_EDATASIZE / 32); // A code is always 32-bits
|
* (VL_EDATASIZE / 32); // A code is always 32-bits
|
||||||
|
|
@ -1239,6 +1252,13 @@ public:
|
||||||
const VNumRange& arrayRange() const { return m_arrayRange; }
|
const VNumRange& arrayRange() const { return m_arrayRange; }
|
||||||
VVarType varType() const { return m_varType; }
|
VVarType varType() const { return m_varType; }
|
||||||
VDirection declDirection() const { return m_declDirection; }
|
VDirection declDirection() const { return m_declDirection; }
|
||||||
|
AstCCall* dtypeCallp() const { return m_dtypeCallp; }
|
||||||
|
void dtypeCallp(AstCCall* const callp) { m_dtypeCallp = callp; }
|
||||||
|
AstVarScope* dtypeVscp() const { return m_dtypeVscp; }
|
||||||
|
void dtypeVscp(AstVarScope* const dtypeVscp) { m_dtypeVscp = dtypeVscp; }
|
||||||
|
bool inDtypeFunc() const { return m_inDtypeFunc; }
|
||||||
|
void dtypeParamName(string dtypeParamName) { m_dtypeParamName = dtypeParamName; }
|
||||||
|
string dtypeParamName() const { return m_dtypeParamName; }
|
||||||
};
|
};
|
||||||
class AstTraceInc final : public AstNodeStmt {
|
class AstTraceInc final : public AstNodeStmt {
|
||||||
// Trace point dump
|
// Trace point dump
|
||||||
|
|
@ -1285,15 +1305,19 @@ public:
|
||||||
class AstTracePushPrefix final : public AstNodeStmt {
|
class AstTracePushPrefix final : public AstNodeStmt {
|
||||||
const string m_prefix; // Prefix to add to signal names
|
const string m_prefix; // Prefix to add to signal names
|
||||||
const VTracePrefixType m_prefixType; // Type of prefix being pushed
|
const VTracePrefixType m_prefixType; // Type of prefix being pushed
|
||||||
|
const bool m_quotedPrefix; // Quote prefix name
|
||||||
public:
|
public:
|
||||||
AstTracePushPrefix(FileLine* fl, const string& prefix, VTracePrefixType prefixType)
|
AstTracePushPrefix(FileLine* fl, const string& prefix, VTracePrefixType prefixType,
|
||||||
|
bool quotedPrefix = true)
|
||||||
: ASTGEN_SUPER_TracePushPrefix(fl)
|
: ASTGEN_SUPER_TracePushPrefix(fl)
|
||||||
, m_prefix{prefix}
|
, m_prefix{prefix}
|
||||||
, m_prefixType{prefixType} {}
|
, m_prefixType{prefixType}
|
||||||
|
, m_quotedPrefix{quotedPrefix} {}
|
||||||
ASTGEN_MEMBERS_AstTracePushPrefix;
|
ASTGEN_MEMBERS_AstTracePushPrefix;
|
||||||
bool sameNode(const AstNode* samep) const override { return false; }
|
bool sameNode(const AstNode* samep) const override { return false; }
|
||||||
string prefix() const { return m_prefix; }
|
string prefix() const { return m_prefix; }
|
||||||
VTracePrefixType prefixType() const { return m_prefixType; }
|
VTracePrefixType prefixType() const { return m_prefixType; }
|
||||||
|
bool quotedPrefix() const { return m_quotedPrefix; }
|
||||||
};
|
};
|
||||||
class AstWait final : public AstNodeStmt {
|
class AstWait final : public AstNodeStmt {
|
||||||
// @astgen op1 := condp : AstNodeExpr
|
// @astgen op1 := condp : AstNodeExpr
|
||||||
|
|
|
||||||
|
|
@ -568,7 +568,7 @@ string AstVar::verilogKwd() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
|
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
|
||||||
bool asRef) const {
|
bool asRef, bool constRef) const {
|
||||||
UASSERT_OBJ(!forReturn, this,
|
UASSERT_OBJ(!forReturn, this,
|
||||||
"Internal data is never passed as return, but as first argument");
|
"Internal data is never passed as return, but as first argument");
|
||||||
string ostatic;
|
string ostatic;
|
||||||
|
|
@ -576,7 +576,7 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
|
||||||
|
|
||||||
asRef = asRef || isDpiOpenArray() || (forFunc && (isWritable() || isRef() || isConstRef()));
|
asRef = asRef || isDpiOpenArray() || (forFunc && (isWritable() || isRef() || isConstRef()));
|
||||||
|
|
||||||
if (forFunc && isReadOnly() && asRef) ostatic = ostatic + "const ";
|
if (forFunc && (isReadOnly() || constRef) && asRef) ostatic = ostatic + "const ";
|
||||||
|
|
||||||
string oname;
|
string oname;
|
||||||
if (named) {
|
if (named) {
|
||||||
|
|
@ -1706,6 +1706,14 @@ string AstBasicDType::prettyDTypeName(bool) const {
|
||||||
}
|
}
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
string AstBasicDType::cDTypeName() const {
|
||||||
|
std::ostringstream os;
|
||||||
|
os << keyword().ascii();
|
||||||
|
if (isRanged() && !rangep() && keyword().width() <= 1) {
|
||||||
|
os << "__BRA__" << cLeft() << "__" << cRight() << "__KET__";
|
||||||
|
}
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
void AstNodeExpr::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
void AstNodeExpr::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||||
void AstNodeExpr::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
void AstNodeExpr::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||||
|
|
@ -2368,6 +2376,7 @@ string AstNodeUOrStructDType::prettyDTypeName(bool full) const {
|
||||||
result += "}" + prettyName();
|
result += "}" + prettyName();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
string AstNodeUOrStructDType::cDTypeName() const { return verilogKwd() + "__" + name(); }
|
||||||
void AstNodeDType::dump(std::ostream& str) const {
|
void AstNodeDType::dump(std::ostream& str) const {
|
||||||
this->AstNode::dump(str);
|
this->AstNode::dump(str);
|
||||||
if (generic()) str << " [GENERIC]";
|
if (generic()) str << " [GENERIC]";
|
||||||
|
|
@ -2426,6 +2435,25 @@ string AstUnpackArrayDType::prettyDTypeName(bool full) const {
|
||||||
os << subp->prettyDTypeName(full) << "$" << ranges;
|
os << subp->prettyDTypeName(full) << "$" << ranges;
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
// NOCOMMIT -- copypastaed from prettyDTypeName() -- is there a better way? encodeName()? name()?
|
||||||
|
string AstPackArrayDType::cDTypeName() const {
|
||||||
|
std::ostringstream os;
|
||||||
|
if (const auto subp = subDTypep()) os << subp->cDTypeName();
|
||||||
|
os << "__BRA__" + cLeft() + "__" + cRight() + "__KET__";
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
string AstUnpackArrayDType::cDTypeName() const {
|
||||||
|
std::ostringstream os;
|
||||||
|
string ranges = "__BRA__" + cLeft() + "__" + cRight() + "__KET__";
|
||||||
|
// See above re: $
|
||||||
|
AstNodeDType* subp = subDTypep()->skipRefp();
|
||||||
|
while (AstUnpackArrayDType* adtypep = VN_CAST(subp, UnpackArrayDType)) {
|
||||||
|
ranges += "__BRA__" + adtypep->cLeft() + "__" + adtypep->cRight() + "__KET__";
|
||||||
|
subp = adtypep->subDTypep()->skipRefp();
|
||||||
|
}
|
||||||
|
os << subp->cDTypeName() << "__024__" << ranges;
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
std::vector<AstUnpackArrayDType*> AstUnpackArrayDType::unpackDimensions() {
|
std::vector<AstUnpackArrayDType*> AstUnpackArrayDType::unpackDimensions() {
|
||||||
std::vector<AstUnpackArrayDType*> dims;
|
std::vector<AstUnpackArrayDType*> dims;
|
||||||
for (AstUnpackArrayDType* unpackp = this; unpackp;) {
|
for (AstUnpackArrayDType* unpackp = this; unpackp;) {
|
||||||
|
|
@ -3092,6 +3120,7 @@ void AstStop::dumpJson(std::ostream& str) const {
|
||||||
void AstTraceDecl::dump(std::ostream& str) const {
|
void AstTraceDecl::dump(std::ostream& str) const {
|
||||||
this->AstNodeStmt::dump(str);
|
this->AstNodeStmt::dump(str);
|
||||||
if (code()) str << " [code=" << code() << "]";
|
if (code()) str << " [code=" << code() << "]";
|
||||||
|
if (dtypeCallp()) str << " [dtypeCallp=" << dtypeCallp() << "]";
|
||||||
}
|
}
|
||||||
void AstTraceDecl::dumpJson(std::ostream& str) const {
|
void AstTraceDecl::dumpJson(std::ostream& str) const {
|
||||||
dumpJsonNumFunc(str, code);
|
dumpJsonNumFunc(str, code);
|
||||||
|
|
|
||||||
|
|
@ -287,7 +287,7 @@ class DeadVisitor final : public VNVisitor {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
checkAll(nodep);
|
checkAll(nodep);
|
||||||
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
if (nodep->scopep()) nodep->scopep()->user1Inc();
|
||||||
if (mightElimVar(nodep->varp())) m_vscsp.push_back(nodep);
|
if (!nodep->tracePreserve() && mightElimVar(nodep->varp())) m_vscsp.push_back(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstVar* nodep) override {
|
void visit(AstVar* nodep) override {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
|
|
||||||
|
|
@ -261,7 +261,8 @@ class DataflowOptimize final {
|
||||||
if (AstVarScope* const vscp = VN_CAST(nodep, VarScope)) {
|
if (AstVarScope* const vscp = VN_CAST(nodep, VarScope)) {
|
||||||
const AstVar* const varp = vscp->varp();
|
const AstVar* const varp = vscp->varp();
|
||||||
// Force and trace have already been processed
|
// Force and trace have already been processed
|
||||||
const bool hasExtRd = varp->isPrimaryIO() || varp->isSigUserRdPublic();
|
const bool hasExtRd = varp->isPrimaryIO() || varp->isSigUserRdPublic()
|
||||||
|
|| vscp->tracePreserve();
|
||||||
const bool hasExtWr = varp->isPrimaryIO() || varp->isSigUserRWPublic();
|
const bool hasExtWr = varp->isPrimaryIO() || varp->isSigUserRWPublic();
|
||||||
if (hasExtRd) DfgVertexVar::setHasExtRdRefs(vscp);
|
if (hasExtRd) DfgVertexVar::setHasExtRdRefs(vscp);
|
||||||
if (hasExtWr) DfgVertexVar::setHasExtWrRefs(vscp);
|
if (hasExtWr) DfgVertexVar::setHasExtWrRefs(vscp);
|
||||||
|
|
|
||||||
|
|
@ -624,6 +624,16 @@ class EmitCTrace final : public EmitCFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
void emitTraceInitOne(const AstTraceDecl* nodep, int enumNum) {
|
void emitTraceInitOne(const AstTraceDecl* nodep, int enumNum) {
|
||||||
|
std::string direction;
|
||||||
|
direction = nodep->declDirection().traceSigDirection();
|
||||||
|
|
||||||
|
AstCCall* const callp = nodep->dtypeCallp();
|
||||||
|
if (callp) {
|
||||||
|
callp->argTypes(callp->argTypes() + ", " + cvtToStr(nodep->fidx()) + ", c+"
|
||||||
|
+ cvtToStr(nodep->code()) + "-1, " + direction);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (nodep->dtypep()->basicp()->isDouble()) {
|
if (nodep->dtypep()->basicp()->isDouble()) {
|
||||||
puts("tracep->declDouble(");
|
puts("tracep->declDouble(");
|
||||||
} else if (nodep->isWide()) {
|
} else if (nodep->isWide()) {
|
||||||
|
|
@ -644,7 +654,11 @@ class EmitCTrace final : public EmitCFunc {
|
||||||
|
|
||||||
// Function index
|
// Function index
|
||||||
puts(",");
|
puts(",");
|
||||||
puts(cvtToStr(nodep->fidx()));
|
if (nodep->inDtypeFunc()) {
|
||||||
|
puts("fidx");
|
||||||
|
} else {
|
||||||
|
puts(cvtToStr(nodep->fidx()));
|
||||||
|
}
|
||||||
|
|
||||||
// Name
|
// Name
|
||||||
puts(",");
|
puts(",");
|
||||||
|
|
@ -654,14 +668,10 @@ class EmitCTrace final : public EmitCFunc {
|
||||||
puts("," + cvtToStr(enumNum));
|
puts("," + cvtToStr(enumNum));
|
||||||
|
|
||||||
// Direction
|
// Direction
|
||||||
if (nodep->declDirection().isInout()) {
|
if (nodep->inDtypeFunc()) {
|
||||||
puts(", VerilatedTraceSigDirection::INOUT");
|
puts(", direction");
|
||||||
} else if (nodep->declDirection().isWritable()) {
|
|
||||||
puts(", VerilatedTraceSigDirection::OUTPUT");
|
|
||||||
} else if (nodep->declDirection().isNonOutput()) {
|
|
||||||
puts(", VerilatedTraceSigDirection::INPUT");
|
|
||||||
} else {
|
} else {
|
||||||
puts(", VerilatedTraceSigDirection::NONE");
|
puts(", " + direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kind
|
// Kind
|
||||||
|
|
@ -752,16 +762,7 @@ class EmitCTrace final : public EmitCFunc {
|
||||||
puts("VL_SC_BV_DATAP(");
|
puts("VL_SC_BV_DATAP(");
|
||||||
}
|
}
|
||||||
iterateConst(varrefp); // Put var name out
|
iterateConst(varrefp); // Put var name out
|
||||||
// Tracing only supports 1D arrays
|
emitTraceIndex(nodep, arrayindex);
|
||||||
if (nodep->declp()->arrayRange().ranged()) {
|
|
||||||
if (arrayindex == -2) {
|
|
||||||
puts("[i]");
|
|
||||||
} else if (arrayindex == -1) {
|
|
||||||
puts("[0]");
|
|
||||||
} else {
|
|
||||||
puts("[" + cvtToStr(arrayindex) + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (varp->isSc()) puts(".read()");
|
if (varp->isSc()) puts(".read()");
|
||||||
if (emitTraceIsScUint(nodep)) {
|
if (emitTraceIsScUint(nodep)) {
|
||||||
puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()");
|
puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()");
|
||||||
|
|
@ -774,10 +775,24 @@ class EmitCTrace final : public EmitCFunc {
|
||||||
} else {
|
} else {
|
||||||
puts("(");
|
puts("(");
|
||||||
iterateConst(nodep->valuep());
|
iterateConst(nodep->valuep());
|
||||||
|
emitTraceIndex(nodep, arrayindex);
|
||||||
puts(")");
|
puts(")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void emitTraceIndex(const AstTraceInc* const nodep, int arrayindex) {
|
||||||
|
// Tracing only supports 1D arrays
|
||||||
|
if (nodep->declp()->arrayRange().ranged()) {
|
||||||
|
if (arrayindex == -2) {
|
||||||
|
puts("[i]");
|
||||||
|
} else if (arrayindex == -1) {
|
||||||
|
puts("[0]");
|
||||||
|
} else {
|
||||||
|
puts("[" + cvtToStr(arrayindex) + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning
|
using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning
|
||||||
void visit(AstCFunc* nodep) override {
|
void visit(AstCFunc* nodep) override {
|
||||||
|
|
@ -797,7 +812,11 @@ class EmitCTrace final : public EmitCFunc {
|
||||||
}
|
}
|
||||||
void visit(AstTracePushPrefix* nodep) override {
|
void visit(AstTracePushPrefix* nodep) override {
|
||||||
putns(nodep, "tracep->pushPrefix(");
|
putns(nodep, "tracep->pushPrefix(");
|
||||||
putsQuoted(VIdProtect::protectWordsIf(nodep->prefix(), nodep->protect()));
|
if (nodep->quotedPrefix()) {
|
||||||
|
putsQuoted(VIdProtect::protectWordsIf(nodep->prefix(), nodep->protect()));
|
||||||
|
} else {
|
||||||
|
puts(nodep->prefix());
|
||||||
|
}
|
||||||
puts(", VerilatedTracePrefixType::");
|
puts(", VerilatedTracePrefixType::");
|
||||||
puts(nodep->prefixType().ascii());
|
puts(nodep->prefixType().ascii());
|
||||||
puts(");\n");
|
puts(");\n");
|
||||||
|
|
|
||||||
|
|
@ -429,11 +429,12 @@ class GateOkVisitor final : public VNVisitorConst {
|
||||||
|
|
||||||
// We only allow a LHS ref for the var being set, and a RHS ref for
|
// We only allow a LHS ref for the var being set, and a RHS ref for
|
||||||
// something else being read.
|
// something else being read.
|
||||||
|
AstVarScope* const vscp = nodep->varScopep();
|
||||||
if (nodep->access().isWriteOnly()) {
|
if (nodep->access().isWriteOnly()) {
|
||||||
|
if (vscp->tracePreserve()) clearSimple("Needed for tracing");
|
||||||
if (m_lhsVarRef) clearSimple(">1 write refs");
|
if (m_lhsVarRef) clearSimple(">1 write refs");
|
||||||
m_lhsVarRef = nodep;
|
m_lhsVarRef = nodep;
|
||||||
} else {
|
} else {
|
||||||
AstVarScope* const vscp = nodep->varScopep();
|
|
||||||
// TODO: possible bug, should it be >= 1 as add is below?
|
// TODO: possible bug, should it be >= 1 as add is below?
|
||||||
if (m_readVscps.size() > 1) {
|
if (m_readVscps.size() > 1) {
|
||||||
if (m_buffersOnly) clearSimple(">1 rhs varRefs");
|
if (m_buffersOnly) clearSimple(">1 rhs varRefs");
|
||||||
|
|
|
||||||
215
src/V3Trace.cpp
215
src/V3Trace.cpp
|
|
@ -39,16 +39,20 @@
|
||||||
|
|
||||||
#include "V3Trace.h"
|
#include "V3Trace.h"
|
||||||
|
|
||||||
|
#include "V3Ast.h"
|
||||||
#include "V3DupFinder.h"
|
#include "V3DupFinder.h"
|
||||||
#include "V3EmitCBase.h"
|
#include "V3EmitCBase.h"
|
||||||
#include "V3Graph.h"
|
#include "V3Graph.h"
|
||||||
#include "V3Stats.h"
|
#include "V3Stats.h"
|
||||||
|
#include "V3UniqueNames.h"
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
||||||
|
// NOCOMMIT -- do runtime bake off
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// Graph vertexes
|
// Graph vertexes
|
||||||
|
|
||||||
|
|
@ -183,6 +187,15 @@ class TraceVisitor final : public VNVisitor {
|
||||||
V3Graph m_graph; // Var/CFunc tracking
|
V3Graph m_graph; // Var/CFunc tracking
|
||||||
TraceActivityVertex* const m_alwaysVtxp; // "Always trace" vertex
|
TraceActivityVertex* const m_alwaysVtxp; // "Always trace" vertex
|
||||||
bool m_finding = false; // Pass one of algorithm?
|
bool m_finding = false; // Pass one of algorithm?
|
||||||
|
struct DtypeFuncs final {
|
||||||
|
public:
|
||||||
|
AstCFunc* fullFuncp = nullptr;
|
||||||
|
AstCFunc* chgFuncp = nullptr;
|
||||||
|
};
|
||||||
|
std::unordered_map<const AstNodeDType*, DtypeFuncs>
|
||||||
|
m_dtypeNonConstFuncs; // Full / Chg funcs per type
|
||||||
|
std::unordered_map<const AstNodeDType*, AstCFunc*> m_dtypeConstFuncs; // Const func per type
|
||||||
|
V3UniqueNames m_dtypeNames{""}; // Unique type func names
|
||||||
|
|
||||||
// Trace parallelism. Only VCD tracing can be parallelized at this time.
|
// Trace parallelism. Only VCD tracing can be parallelized at this time.
|
||||||
const uint32_t m_parallelism
|
const uint32_t m_parallelism
|
||||||
|
|
@ -207,13 +220,13 @@ class TraceVisitor final : public VNVisitor {
|
||||||
// Hash all of the traced values and find if there are any duplicates
|
// Hash all of the traced values and find if there are any duplicates
|
||||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||||
if (TraceTraceVertex* const vvertexp = vtx.cast<TraceTraceVertex>()) {
|
if (TraceTraceVertex* const vvertexp = vtx.cast<TraceTraceVertex>()) {
|
||||||
const AstTraceDecl* const nodep = vvertexp->nodep();
|
AstTraceDecl* const nodep = vvertexp->nodep();
|
||||||
UASSERT_OBJ(!vvertexp->duplicatep(), nodep, "Should not be a duplicate");
|
UASSERT_OBJ(!vvertexp->duplicatep(), nodep, "Should not be a duplicate");
|
||||||
const auto dupit = dupFinder.findDuplicate(nodep->valuep());
|
const auto dupit = dupFinder.findDuplicate(nodep);
|
||||||
if (dupit == dupFinder.end()) {
|
if (dupit == dupFinder.end()) {
|
||||||
dupFinder.insert(nodep->valuep());
|
dupFinder.insert(nodep);
|
||||||
} else {
|
} else {
|
||||||
const AstTraceDecl* const dupDeclp = VN_AS(dupit->second->backp(), TraceDecl);
|
const AstTraceDecl* const dupDeclp = VN_AS(dupit->second, TraceDecl);
|
||||||
UASSERT_OBJ(dupDeclp, nodep, "Trace duplicate of wrong type");
|
UASSERT_OBJ(dupDeclp, nodep, "Trace duplicate of wrong type");
|
||||||
TraceTraceVertex* const dupvertexp
|
TraceTraceVertex* const dupvertexp
|
||||||
= dupDeclp->user1u().toGraphVertex()->cast<TraceTraceVertex>();
|
= dupDeclp->user1u().toGraphVertex()->cast<TraceTraceVertex>();
|
||||||
|
|
@ -484,11 +497,11 @@ class TraceVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
AstCFunc* newCFunc(VTraceType traceType, AstCFunc* topFuncp, uint32_t funcNum,
|
AstCFunc* newCFunc(VTraceType traceType, AstCFunc* topFuncp, uint32_t funcNum,
|
||||||
uint32_t baseCode = 0) {
|
uint32_t baseCode = 0, const AstTraceDecl* const declp = nullptr) {
|
||||||
// Create new function
|
// Create new function
|
||||||
const bool isTopFunc = topFuncp == nullptr;
|
const bool isTopFunc = !declp && topFuncp == nullptr;
|
||||||
std::string funcName;
|
std::string funcName;
|
||||||
if (isTopFunc) {
|
if (isTopFunc || declp) {
|
||||||
if (traceType == VTraceType::CONSTANT) {
|
if (traceType == VTraceType::CONSTANT) {
|
||||||
funcName = "trace_const";
|
funcName = "trace_const";
|
||||||
} else if (traceType == VTraceType::FULL) {
|
} else if (traceType == VTraceType::FULL) {
|
||||||
|
|
@ -500,8 +513,14 @@ class TraceVisitor final : public VNVisitor {
|
||||||
funcName = topFuncp->name();
|
funcName = topFuncp->name();
|
||||||
funcName += "_sub";
|
funcName += "_sub";
|
||||||
}
|
}
|
||||||
funcName += "_";
|
if (declp) {
|
||||||
funcName += cvtToStr(funcNum);
|
funcName += "_dtype_";
|
||||||
|
funcName += declp->valuep()->dtypep()->cDTypeName();
|
||||||
|
funcName = m_dtypeNames.get(funcName);
|
||||||
|
} else {
|
||||||
|
funcName += "_";
|
||||||
|
funcName += cvtToStr(funcNum);
|
||||||
|
}
|
||||||
|
|
||||||
FileLine* const flp = m_topScopep->fileline();
|
FileLine* const flp = m_topScopep->fileline();
|
||||||
AstCFunc* const funcp = new AstCFunc{flp, funcName, m_topScopep};
|
AstCFunc* const funcp = new AstCFunc{flp, funcName, m_topScopep};
|
||||||
|
|
@ -514,7 +533,8 @@ class TraceVisitor final : public VNVisitor {
|
||||||
m_topScopep->addBlocksp(funcp);
|
m_topScopep->addBlocksp(funcp);
|
||||||
const std::string bufArg
|
const std::string bufArg
|
||||||
= v3Global.opt.traceClassBase()
|
= v3Global.opt.traceClassBase()
|
||||||
+ "::" + (v3Global.opt.useTraceOffload() ? "OffloadBuffer" : "Buffer") + "* bufp";
|
+ "::" + (v3Global.opt.useTraceOffload() ? "OffloadBuffer" : "Buffer") + "* bufp"
|
||||||
|
+ (declp ? (", uint32_t offset, " + declp->dtypeParamName()) : "");
|
||||||
if (isTopFunc) {
|
if (isTopFunc) {
|
||||||
// Top functions
|
// Top functions
|
||||||
funcp->argTypes("void* voidSelf, " + bufArg);
|
funcp->argTypes("void* voidSelf, " + bufArg);
|
||||||
|
|
@ -549,35 +569,62 @@ class TraceVisitor final : public VNVisitor {
|
||||||
if (traceType != VTraceType::CHANGE) {
|
if (traceType != VTraceType::CHANGE) {
|
||||||
// Full dump sub function
|
// Full dump sub function
|
||||||
funcp->addStmtsp(new AstCStmt{flp, //
|
funcp->addStmtsp(new AstCStmt{flp, //
|
||||||
"uint32_t* const oldp VL_ATTR_UNUSED = "
|
string("uint32_t* const oldp VL_ATTR_UNUSED = "
|
||||||
"bufp->oldp(vlSymsp->__Vm_baseCode);\n"});
|
"bufp->oldp(vlSymsp->__Vm_baseCode")
|
||||||
|
+ (declp ? " + offset - 1" : "") + ");\n"});
|
||||||
} else {
|
} else {
|
||||||
// Change dump sub function
|
// Change dump sub function
|
||||||
if (v3Global.opt.useTraceOffload()) {
|
if (v3Global.opt.useTraceOffload()) {
|
||||||
funcp->addStmtsp(new AstCStmt{flp, //
|
funcp->addStmtsp(
|
||||||
"const uint32_t base VL_ATTR_UNUSED = "
|
new AstCStmt{flp, //
|
||||||
"vlSymsp->__Vm_baseCode + "
|
"const uint32_t base VL_ATTR_UNUSED = "
|
||||||
+ cvtToStr(baseCode) + ";\n"});
|
"vlSymsp->__Vm_baseCode + "
|
||||||
|
+ (declp ? " offset - 1" : cvtToStr(baseCode)) + ";\n"});
|
||||||
funcp->addStmtsp(
|
funcp->addStmtsp(
|
||||||
new AstCStmt{flp, "(void)bufp; // Prevent unused variable warning\n"});
|
new AstCStmt{flp, "(void)bufp; // Prevent unused variable warning\n"});
|
||||||
} else {
|
} else {
|
||||||
funcp->addStmtsp(new AstCStmt{flp, //
|
funcp->addStmtsp(
|
||||||
"uint32_t* const oldp VL_ATTR_UNUSED = "
|
new AstCStmt{flp, //
|
||||||
"bufp->oldp(vlSymsp->__Vm_baseCode + "
|
"uint32_t* const oldp VL_ATTR_UNUSED = "
|
||||||
+ cvtToStr(baseCode) + ");\n"});
|
"bufp->oldp(vlSymsp->__Vm_baseCode + "
|
||||||
|
+ (declp ? " offset - 1" : cvtToStr(baseCode)) + ");\n"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add call to top function
|
if (!declp) {
|
||||||
AstCCall* const callp = new AstCCall{funcp->fileline(), funcp};
|
// Add call to top function
|
||||||
callp->dtypeSetVoid();
|
AstCCall* const callp = new AstCCall{funcp->fileline(), funcp};
|
||||||
callp->argTypes("bufp");
|
callp->dtypeSetVoid();
|
||||||
topFuncp->addStmtsp(callp->makeStmt());
|
callp->argTypes("bufp");
|
||||||
|
topFuncp->addStmtsp(callp->makeStmt());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Done
|
// Done
|
||||||
UINFO(5, " newCFunc " << funcp);
|
UINFO(5, " newCFunc " << funcp);
|
||||||
return funcp;
|
return funcp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AstCFunc* createConstDtypeTraceFunctions(const AstTraceDecl* declp) {
|
||||||
|
const AstNodeDType* const dtypep = declp->valuep()->dtypep()->skipRefp();
|
||||||
|
auto pair = m_dtypeConstFuncs.emplace(dtypep, nullptr);
|
||||||
|
if (pair.second) {
|
||||||
|
FileLine* const flp = declp->fileline();
|
||||||
|
AstCFunc* const funcp = newCFunc(VTraceType::CONSTANT, nullptr, 0, 0, declp);
|
||||||
|
|
||||||
|
for (AstNode* stmtp = declp->dtypeCallp()->funcp()->stmtsp(); stmtp;
|
||||||
|
stmtp = stmtp->nextp()) {
|
||||||
|
if (AstTraceDecl* const fieldDeclp = VN_CAST(stmtp, TraceDecl)) {
|
||||||
|
AstTraceInc* const incp
|
||||||
|
= new AstTraceInc{flp, fieldDeclp, VTraceType::CONSTANT};
|
||||||
|
funcp->addStmtsp(incp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pair.first->second = funcp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pair.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
void createConstTraceFunctions(const TraceVec& traces) {
|
void createConstTraceFunctions(const TraceVec& traces) {
|
||||||
const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace()
|
const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace()
|
||||||
: std::numeric_limits<int>::max();
|
: std::numeric_limits<int>::max();
|
||||||
|
|
@ -598,6 +645,7 @@ class TraceVisitor final : public VNVisitor {
|
||||||
UASSERT_OBJ(canonDeclp->code() != 0, canonDeclp,
|
UASSERT_OBJ(canonDeclp->code() != 0, canonDeclp,
|
||||||
"Canonical node should have code assigned already");
|
"Canonical node should have code assigned already");
|
||||||
declp->code(canonDeclp->code());
|
declp->code(canonDeclp->code());
|
||||||
|
declp->dtypeVscp(nullptr);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -620,13 +668,55 @@ class TraceVisitor final : public VNVisitor {
|
||||||
++subFuncNum;
|
++subFuncNum;
|
||||||
}
|
}
|
||||||
FileLine* const flp = declp->fileline();
|
FileLine* const flp = declp->fileline();
|
||||||
AstTraceInc* const incp = new AstTraceInc{flp, declp, VTraceType::CONSTANT};
|
if (declp->dtypeCallp()) {
|
||||||
subFuncp->addStmtsp(incp);
|
AstCFunc* const funcp = createConstDtypeTraceFunctions(declp);
|
||||||
subStmts += incp->nodeCount();
|
AstVarRef* argsp = nullptr;
|
||||||
|
argsp = AstNode::addNext(
|
||||||
|
argsp, new AstVarRef{flp, declp->dtypeVscp(), VAccess::READ});
|
||||||
|
AstCCall* const callp = new AstCCall{flp, funcp, argsp};
|
||||||
|
callp->dtypeSetVoid();
|
||||||
|
callp->argTypes(callp->argTypes() + "bufp, " + std::to_string(declp->code()));
|
||||||
|
subFuncp->addStmtsp(callp->makeStmt());
|
||||||
|
|
||||||
|
declp->dtypeVscp(nullptr);
|
||||||
|
|
||||||
|
// NOCOMMIT -- ????
|
||||||
|
subStmts += 1;
|
||||||
|
} else {
|
||||||
|
AstTraceInc* const incp = new AstTraceInc{flp, declp, VTraceType::CONSTANT};
|
||||||
|
subFuncp->addStmtsp(incp);
|
||||||
|
subStmts += incp->nodeCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DtypeFuncs createNonConstDtypeTraceFunctions(const AstTraceDecl* declp) {
|
||||||
|
AstNodeDType* dtypep = declp->valuep()->dtypep()->skipRefp();
|
||||||
|
auto pair = m_dtypeNonConstFuncs.emplace(dtypep, DtypeFuncs{});
|
||||||
|
if (pair.second) {
|
||||||
|
FileLine* const flp = declp->fileline();
|
||||||
|
AstCFunc* const fullFuncp = newCFunc(VTraceType::FULL, nullptr, 0, 0, declp);
|
||||||
|
AstCFunc* const chgFuncp = newCFunc(VTraceType::CHANGE, nullptr, 0, 0, declp);
|
||||||
|
|
||||||
|
for (AstNode* stmtp = declp->dtypeCallp()->funcp()->stmtsp(); stmtp;
|
||||||
|
stmtp = stmtp->nextp()) {
|
||||||
|
if (AstTraceDecl* const fieldDeclp = VN_CAST(stmtp, TraceDecl)) {
|
||||||
|
AstTraceInc* const incFullp
|
||||||
|
= new AstTraceInc{flp, fieldDeclp, VTraceType::FULL};
|
||||||
|
fullFuncp->addStmtsp(incFullp);
|
||||||
|
AstTraceInc* const incChgp
|
||||||
|
= new AstTraceInc{flp, fieldDeclp, VTraceType::CHANGE};
|
||||||
|
chgFuncp->addStmtsp(incChgp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pair.first->second = {.fullFuncp = fullFuncp, .chgFuncp = chgFuncp};
|
||||||
|
}
|
||||||
|
|
||||||
|
return pair.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
void createNonConstTraceFunctions(const TraceVec& traces, uint32_t nAllCodes,
|
void createNonConstTraceFunctions(const TraceVec& traces, uint32_t nAllCodes,
|
||||||
uint32_t parallelism) {
|
uint32_t parallelism) {
|
||||||
const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace()
|
const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace()
|
||||||
|
|
@ -659,6 +749,7 @@ class TraceVisitor final : public VNVisitor {
|
||||||
// function index to the same as the canonical node.
|
// function index to the same as the canonical node.
|
||||||
if (const TraceTraceVertex* const canonVtxp = vtxp->duplicatep()) {
|
if (const TraceTraceVertex* const canonVtxp = vtxp->duplicatep()) {
|
||||||
declp->fidx(canonVtxp->nodep()->fidx());
|
declp->fidx(canonVtxp->nodep()->fidx());
|
||||||
|
declp->dtypeVscp(nullptr);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -680,6 +771,7 @@ class TraceVisitor final : public VNVisitor {
|
||||||
ifp = nullptr;
|
ifp = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOCOMMIT -- is it OK to do this only on the aggregate signal?
|
||||||
// If required, create the conditional node checking the activity flags
|
// If required, create the conditional node checking the activity flags
|
||||||
if (!prevActSet || actSet != *prevActSet) {
|
if (!prevActSet || actSet != *prevActSet) {
|
||||||
FileLine* const flp = m_topScopep->fileline();
|
FileLine* const flp = m_topScopep->fileline();
|
||||||
|
|
@ -702,24 +794,53 @@ class TraceVisitor final : public VNVisitor {
|
||||||
|
|
||||||
// Add TraceInc nodes
|
// Add TraceInc nodes
|
||||||
FileLine* const flp = declp->fileline();
|
FileLine* const flp = declp->fileline();
|
||||||
AstTraceInc* const incFulp = new AstTraceInc{flp, declp, VTraceType::FULL};
|
if (declp->dtypeCallp()) {
|
||||||
subFulFuncp->addStmtsp(incFulp);
|
DtypeFuncs funcs = createNonConstDtypeTraceFunctions(declp);
|
||||||
AstTraceInc* const incChgp
|
AstVarRef* argsp = nullptr;
|
||||||
= new AstTraceInc{flp, declp, VTraceType::CHANGE, baseCode};
|
argsp = AstNode::addNext(
|
||||||
ifp->addThensp(incChgp);
|
argsp, new AstVarRef{flp, declp->dtypeVscp(), VAccess::READ});
|
||||||
|
AstCCall* const callFullp = new AstCCall{flp, funcs.fullFuncp, argsp};
|
||||||
|
callFullp->dtypeSetVoid();
|
||||||
|
callFullp->argTypes(callFullp->argTypes() + "bufp, "
|
||||||
|
+ std::to_string(declp->code()));
|
||||||
|
subFulFuncp->addStmtsp(callFullp->makeStmt());
|
||||||
|
argsp = nullptr;
|
||||||
|
argsp = AstNode::addNext(
|
||||||
|
argsp, new AstVarRef{flp, declp->dtypeVscp(), VAccess::READ});
|
||||||
|
AstCCall* const callChgp = new AstCCall{flp, funcs.chgFuncp, argsp};
|
||||||
|
callChgp->dtypeSetVoid();
|
||||||
|
callChgp->argTypes(callChgp->argTypes()
|
||||||
|
+ "bufp, "
|
||||||
|
// NOCOMMIT -- some kind of two-wrongs off-by-one error
|
||||||
|
// somewhere: really seems like it should be code() - 1 for
|
||||||
|
// the chg func (see old chg func)
|
||||||
|
+ std::to_string(declp->code()));
|
||||||
|
ifp->addThensp(callChgp->makeStmt());
|
||||||
|
|
||||||
// Set the function index of the decl
|
declp->dtypeVscp(nullptr);
|
||||||
declp->fidx(topFuncNum);
|
|
||||||
|
|
||||||
// Track splitting due to size
|
// NOCOMMIT -- ????
|
||||||
UASSERT_OBJ(incFulp->nodeCount() == incChgp->nodeCount(), declp,
|
subStmts += 2;
|
||||||
"Should have equal cost");
|
|
||||||
const VNumRange range = declp->arrayRange();
|
|
||||||
if (range.ranged()) {
|
|
||||||
// 2x because each element is a TraceInc and a VarRef
|
|
||||||
subStmts += range.elements() * 2;
|
|
||||||
} else {
|
} else {
|
||||||
subStmts += incChgp->nodeCount();
|
AstTraceInc* const incFulp = new AstTraceInc{flp, declp, VTraceType::FULL};
|
||||||
|
subFulFuncp->addStmtsp(incFulp);
|
||||||
|
AstTraceInc* const incChgp
|
||||||
|
= new AstTraceInc{flp, declp, VTraceType::CHANGE, baseCode};
|
||||||
|
ifp->addThensp(incChgp);
|
||||||
|
|
||||||
|
// Set the function index of the decl
|
||||||
|
declp->fidx(topFuncNum);
|
||||||
|
|
||||||
|
// Track splitting due to size
|
||||||
|
UASSERT_OBJ(incFulp->nodeCount() == incChgp->nodeCount(), declp,
|
||||||
|
"Should have equal cost");
|
||||||
|
const VNumRange range = declp->arrayRange();
|
||||||
|
if (range.ranged()) {
|
||||||
|
// 2x because each element is a TraceInc and a VarRef
|
||||||
|
subStmts += range.elements() * 2;
|
||||||
|
} else {
|
||||||
|
subStmts += incChgp->nodeCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track partitioning
|
// Track partitioning
|
||||||
|
|
@ -899,7 +1020,7 @@ class TraceVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
void visit(AstTraceDecl* nodep) override {
|
void visit(AstTraceDecl* nodep) override {
|
||||||
UINFO(8, " TRACE " << nodep);
|
UINFO(8, " TRACE " << nodep);
|
||||||
if (!m_finding) {
|
if (!m_finding && !nodep->inDtypeFunc()) {
|
||||||
V3GraphVertex* const vertexp = new TraceTraceVertex{&m_graph, nodep};
|
V3GraphVertex* const vertexp = new TraceTraceVertex{&m_graph, nodep};
|
||||||
nodep->user1p(vertexp);
|
nodep->user1p(vertexp);
|
||||||
|
|
||||||
|
|
@ -941,6 +1062,12 @@ public:
|
||||||
explicit TraceVisitor(AstNetlist* nodep)
|
explicit TraceVisitor(AstNetlist* nodep)
|
||||||
: m_alwaysVtxp{new TraceActivityVertex{&m_graph, TraceActivityVertex::ACTIVITY_ALWAYS}} {
|
: m_alwaysVtxp{new TraceActivityVertex{&m_graph, TraceActivityVertex::ACTIVITY_ALWAYS}} {
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
|
nodep->foreach([](AstTraceDecl* const declp) {
|
||||||
|
if (declp->inDtypeFunc()) {
|
||||||
|
declp->valuep()->unlinkFrBack()->deleteTree();
|
||||||
|
declp->valuep(nullptr);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
~TraceVisitor() override {
|
~TraceVisitor() override {
|
||||||
V3Stats::addStat("Tracing, Activity setters", m_statSetters);
|
V3Stats::addStat("Tracing, Activity setters", m_statSetters);
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,20 @@
|
||||||
|
|
||||||
#include "V3TraceDecl.h"
|
#include "V3TraceDecl.h"
|
||||||
|
|
||||||
|
#include "V3Ast.h"
|
||||||
#include "V3Control.h"
|
#include "V3Control.h"
|
||||||
#include "V3EmitCBase.h"
|
#include "V3EmitCBase.h"
|
||||||
|
#include "V3Error.h"
|
||||||
|
#include "V3File.h"
|
||||||
|
#include "V3Global.h"
|
||||||
|
#include "V3Number.h"
|
||||||
#include "V3Stats.h"
|
#include "V3Stats.h"
|
||||||
|
#include "V3UniqueNames.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
@ -90,6 +97,8 @@ public:
|
||||||
|
|
||||||
class TraceDeclVisitor final : public VNVisitor {
|
class TraceDeclVisitor final : public VNVisitor {
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
|
// AstCFunc::user1() // code offset for current type
|
||||||
|
// AstCFunc::user2() // VarScope for dtype functions
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
AstTopScope* const m_topScopep; // The singleton AstTopScope
|
AstTopScope* const m_topScopep; // The singleton AstTopScope
|
||||||
|
|
@ -98,6 +107,11 @@ class TraceDeclVisitor final : public VNVisitor {
|
||||||
std::vector<AstCFunc*> m_topFuncps; // Top level trace initialization functions
|
std::vector<AstCFunc*> m_topFuncps; // Top level trace initialization functions
|
||||||
std::vector<AstCFunc*> m_subFuncps; // Trace sub functions for this scope
|
std::vector<AstCFunc*> m_subFuncps; // Trace sub functions for this scope
|
||||||
std::set<const AstTraceDecl*> m_declUncalledps; // Declarations not called
|
std::set<const AstTraceDecl*> m_declUncalledps; // Declarations not called
|
||||||
|
std::unordered_map<const AstNodeDType*, AstCFunc*> m_dtypeFuncs; // Functions per type
|
||||||
|
AstCFunc* m_dtypeFunc = nullptr; // Current type func
|
||||||
|
V3UniqueNames m_dtypeNames{""}; // Unique names for dtype funcs
|
||||||
|
bool m_skipDtypeFunc = false; // Don't create a type func
|
||||||
|
int m_offset = 0; // Offset for types
|
||||||
int m_topFuncSize = 0; // Size of the top function currently being built
|
int m_topFuncSize = 0; // Size of the top function currently being built
|
||||||
int m_subFuncSize = 0; // Size of the sub function currently being built
|
int m_subFuncSize = 0; // Size of the sub function currently being built
|
||||||
const int m_funcSizeLimit // Maximum size of a function
|
const int m_funcSizeLimit // Maximum size of a function
|
||||||
|
|
@ -222,6 +236,11 @@ class TraceDeclVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
void addToSubFunc(AstNodeStmt* stmtp) {
|
void addToSubFunc(AstNodeStmt* stmtp) {
|
||||||
|
// TODO -- sub funcs for dtype components
|
||||||
|
if (m_dtypeFunc) {
|
||||||
|
m_dtypeFunc->addStmtsp(stmtp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (m_subFuncSize > m_funcSizeLimit || m_subFuncps.empty()) {
|
if (m_subFuncSize > m_funcSizeLimit || m_subFuncps.empty()) {
|
||||||
m_subFuncSize = 0;
|
m_subFuncSize = 0;
|
||||||
//
|
//
|
||||||
|
|
@ -236,20 +255,49 @@ class TraceDeclVisitor final : public VNVisitor {
|
||||||
m_subFuncSize += stmtp->nodeCount();
|
m_subFuncSize += stmtp->nodeCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addTraceDecl(const VNumRange& arrayRange,
|
AstTraceDecl* addTraceDecl(const VNumRange& arrayRange,
|
||||||
int widthOverride) { // If !=0, is packed struct/array where basicp size
|
int widthOverride, // If !=0, is packed struct/array where basicp
|
||||||
// misreflects one element
|
// size misreflects one element
|
||||||
|
AstCCall* const dtypeCallp = nullptr) {
|
||||||
VNumRange bitRange;
|
VNumRange bitRange;
|
||||||
if (widthOverride) {
|
if (widthOverride) {
|
||||||
bitRange = VNumRange{widthOverride - 1, 0};
|
bitRange = VNumRange{widthOverride - 1, 0};
|
||||||
} else if (const AstBasicDType* const bdtypep = m_traValuep->dtypep()->basicp()) {
|
} else if (const AstBasicDType* const bdtypep = m_traValuep->dtypep()->basicp()) {
|
||||||
bitRange = bdtypep->nrange();
|
bitRange = bdtypep->nrange();
|
||||||
}
|
}
|
||||||
AstTraceDecl* const newp
|
FileLine* const flp = m_traVscp->fileline();
|
||||||
= new AstTraceDecl{m_traVscp->fileline(), m_traName, m_traVscp->varp(),
|
AstNodeExpr* valuep = m_traValuep->cloneTree(false);
|
||||||
m_traValuep->cloneTree(false), bitRange, arrayRange};
|
AstTraceDecl* const newp = new AstTraceDecl{
|
||||||
|
flp, m_traName, m_traVscp->varp(), valuep,
|
||||||
|
bitRange, arrayRange, dtypeCallp, dtypeCallp ? m_traVscp : nullptr,
|
||||||
|
m_offset != 0};
|
||||||
|
// NOCOMMIT -- m_offset and may be redundant with something else here ^
|
||||||
|
if (m_offset) {
|
||||||
|
newp->code(m_offset);
|
||||||
|
if (!dtypeCallp) { m_offset += newp->codeInc(); }
|
||||||
|
valuep->foreach([&](AstVarRef* const refp) {
|
||||||
|
UASSERT_OBJ(refp->varScopep() == m_traVscp, refp,
|
||||||
|
"Trace decl expression references unexpected var");
|
||||||
|
refp->replaceWith(new AstCExpr{flp, VIdProtect::protect(m_traVscp->varp()->name()),
|
||||||
|
m_traVscp->width()});
|
||||||
|
VL_DO_DANGLING(refp->deleteTree(), refp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (dtypeCallp) {
|
||||||
|
// NOCOMMIT -- are both necessary?
|
||||||
|
m_traVscp->tracePreserve(true);
|
||||||
|
m_traVscp->varp()->trace(true);
|
||||||
|
m_traVscp->varp()->sigPublic(true); // NOCOMMIT -- this is a lie -- FIX
|
||||||
|
// NOCOMMIT -- adding this because const and non-const func param names conflict --
|
||||||
|
// probably a better way
|
||||||
|
newp->dtypeParamName(VN_AS(dtypeCallp->funcp()->user2p(), VarScope)
|
||||||
|
->varp()
|
||||||
|
->vlArgType(true, false, true, "", true, true));
|
||||||
|
}
|
||||||
m_declUncalledps.emplace(newp);
|
m_declUncalledps.emplace(newp);
|
||||||
addToSubFunc(newp);
|
addToSubFunc(newp);
|
||||||
|
|
||||||
|
return newp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addIgnore(const string& why) {
|
void addIgnore(const string& why) {
|
||||||
|
|
@ -421,6 +469,7 @@ class TraceDeclVisitor final : public VNVisitor {
|
||||||
= new AstVarRef{m_traVscp->fileline(), m_traVscp, VAccess::READ};
|
= new AstVarRef{m_traVscp->fileline(), m_traVscp, VAccess::READ};
|
||||||
// Recurse into data type of the signal. The visit methods will add
|
// Recurse into data type of the signal. The visit methods will add
|
||||||
// AstTraceDecls.
|
// AstTraceDecls.
|
||||||
|
VL_RESTORER(m_offset);
|
||||||
iterate(m_traVscp->varp()->dtypep()->skipRefToEnump());
|
iterate(m_traVscp->varp()->dtypep()->skipRefToEnump());
|
||||||
// Delete reference created above. Traversal cloned it as required.
|
// Delete reference created above. Traversal cloned it as required.
|
||||||
if (m_traValuep) {
|
if (m_traValuep) {
|
||||||
|
|
@ -503,10 +552,14 @@ class TraceDeclVisitor final : public VNVisitor {
|
||||||
// VISITORS - Data types when tracing
|
// VISITORS - Data types when tracing
|
||||||
void visit(AstConstDType* nodep) override {
|
void visit(AstConstDType* nodep) override {
|
||||||
if (!m_traVscp) return;
|
if (!m_traVscp) return;
|
||||||
|
VL_RESTORER(m_offset);
|
||||||
|
VL_RESTORER(m_skipDtypeFunc);
|
||||||
|
m_skipDtypeFunc = true;
|
||||||
iterate(nodep->subDTypep()->skipRefToEnump());
|
iterate(nodep->subDTypep()->skipRefToEnump());
|
||||||
}
|
}
|
||||||
void visit(AstRefDType* nodep) override {
|
void visit(AstRefDType* nodep) override {
|
||||||
if (!m_traVscp) return;
|
if (!m_traVscp) return;
|
||||||
|
VL_RESTORER(m_offset);
|
||||||
iterate(nodep->subDTypep()->skipRefToEnump());
|
iterate(nodep->subDTypep()->skipRefToEnump());
|
||||||
}
|
}
|
||||||
void visit(AstIfaceRefDType* nodep) override {
|
void visit(AstIfaceRefDType* nodep) override {
|
||||||
|
|
@ -518,20 +571,63 @@ class TraceDeclVisitor final : public VNVisitor {
|
||||||
addToSubFunc(stmtp);
|
addToSubFunc(stmtp);
|
||||||
m_ifaceRefInitPlaceholders.emplace_back(m_traVscp, stmtp);
|
m_ifaceRefInitPlaceholders.emplace_back(m_traVscp, stmtp);
|
||||||
}
|
}
|
||||||
void visit(AstUnpackArrayDType* nodep) override {
|
void newDeclFunc(AstNodeDType* nodep) {
|
||||||
// Note more specific dtypes above
|
AstNodeDType* const skipTypep = nodep->skipRefp();
|
||||||
if (!m_traVscp) return;
|
// offset and direction args added in EmitCImp
|
||||||
|
std::string callArgs{"tracep, \"" + VIdProtect::protect(m_traName) + "\""};
|
||||||
|
VL_RESTORER(m_traName);
|
||||||
|
FileLine* const flp = skipTypep->fileline();
|
||||||
|
|
||||||
if (v3Global.opt.traceMaxArray()
|
auto pair = m_dtypeFuncs.emplace(skipTypep, nullptr);
|
||||||
&& static_cast<int>(nodep->arrayUnpackedElements()) > v3Global.opt.traceMaxArray()) {
|
AstCFunc** funcpp = &pair.first->second;
|
||||||
addIgnore("Wide memory > --trace-max-array ents");
|
if (pair.second) {
|
||||||
return;
|
string dtypeName = skipTypep->cDTypeName();
|
||||||
|
const string name{"trace_init_dtype__" + dtypeName};
|
||||||
|
// NOCOMMIT -- should we only use V3UniqueNames instead of worrying about cDTypeName()?
|
||||||
|
*funcpp = newCFunc(flp, m_dtypeNames.get(name));
|
||||||
|
(*funcpp)->user2p(m_traVscp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AstCCall* const callp = new AstCCall{flp, *funcpp};
|
||||||
|
callp->dtypeSetVoid();
|
||||||
|
callp->argTypes(callArgs);
|
||||||
|
AstTraceDecl* const declp = addTraceDecl(VNumRange{}, skipTypep->width(), callp);
|
||||||
|
addToSubFunc(callp->makeStmt());
|
||||||
|
|
||||||
|
if (pair.second) {
|
||||||
|
VL_RESTORER(m_offset);
|
||||||
|
m_offset = 1;
|
||||||
|
|
||||||
|
VL_RESTORER(m_dtypeFunc);
|
||||||
|
m_dtypeFunc = *funcpp;
|
||||||
|
m_dtypeFunc->argTypes(m_dtypeFunc->argTypes()
|
||||||
|
+ ", const char* name, uint32_t fidx, uint32_t c, "
|
||||||
|
"VerilatedTraceSigDirection direction");
|
||||||
|
if (AstStructDType* const dtypep = VN_CAST(skipTypep, StructDType)) {
|
||||||
|
declStruct(dtypep, true);
|
||||||
|
} else if (AstUnpackArrayDType* const dtypep = VN_CAST(skipTypep, UnpackArrayDType)) {
|
||||||
|
declUnpackedArray(dtypep, true);
|
||||||
|
} else if (AstPackArrayDType* const dtypep = VN_CAST(skipTypep, PackArrayDType)) {
|
||||||
|
declPackedArray(dtypep, true);
|
||||||
|
} else {
|
||||||
|
UASSERT_OBJ(false, skipTypep, "Creating a trace function for an unexpected type");
|
||||||
|
}
|
||||||
|
// Code 0 is a sentinel value
|
||||||
|
// NOCOMMIT -- handle that ^ so we don't need the -1's?'
|
||||||
|
m_dtypeFunc->user1(m_offset - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
declp->codeInc((*funcpp)->user1());
|
||||||
|
m_offset += declp->codeInc();
|
||||||
|
}
|
||||||
|
void declUnpackedArray(AstUnpackArrayDType* const nodep, bool newFunc) {
|
||||||
|
string prefixName(newFunc ? "name" : m_traName);
|
||||||
|
|
||||||
VL_RESTORER(m_traName);
|
VL_RESTORER(m_traName);
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
|
|
||||||
addToSubFunc(new AstTracePushPrefix{flp, m_traName, VTracePrefixType::ARRAY_UNPACKED});
|
addToSubFunc(
|
||||||
|
new AstTracePushPrefix{flp, prefixName, VTracePrefixType::ARRAY_UNPACKED, !newFunc});
|
||||||
|
|
||||||
if (VN_IS(nodep->subDTypep()->skipRefToEnump(),
|
if (VN_IS(nodep->subDTypep()->skipRefToEnump(),
|
||||||
BasicDType) // Nothing lower than this array
|
BasicDType) // Nothing lower than this array
|
||||||
|
|
@ -559,20 +655,39 @@ class TraceDeclVisitor final : public VNVisitor {
|
||||||
|
|
||||||
addToSubFunc(new AstTracePopPrefix{flp});
|
addToSubFunc(new AstTracePopPrefix{flp});
|
||||||
}
|
}
|
||||||
void visit(AstPackArrayDType* nodep) override {
|
// NOCOMMIT -- how to handle VL_* macro'ed types?
|
||||||
|
bool isBasicIO() {
|
||||||
|
const AstVar* varp = m_traVscp->varp();
|
||||||
|
const AstBasicDType* basicp = varp->basicp();
|
||||||
|
// NOCOMMIT -- lifted from V3EmitCBase -- AstVar method?
|
||||||
|
return varp->isIO() && basicp && !basicp->isOpaque();
|
||||||
|
}
|
||||||
|
void visit(AstUnpackArrayDType* nodep) override {
|
||||||
|
// Note more specific dtypes above
|
||||||
if (!m_traVscp) return;
|
if (!m_traVscp) return;
|
||||||
|
|
||||||
if (!v3Global.opt.traceStructs()) {
|
if (v3Global.opt.traceMaxArray()
|
||||||
// Everything downstream is packed, so deal with as one trace unit.
|
&& static_cast<int>(nodep->arrayUnpackedElements()) > v3Global.opt.traceMaxArray()) {
|
||||||
// This may not be the nicest for user presentation, but is
|
addIgnore("Wide memory > --trace-max-array ents");
|
||||||
// a much faster way to trace
|
|
||||||
addTraceDecl(VNumRange{}, nodep->width());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VL_RESTORER(m_skipDtypeFunc);
|
||||||
|
if (isBasicIO()) m_skipDtypeFunc = true;
|
||||||
|
|
||||||
|
if (m_skipDtypeFunc || m_dtypeFunc) {
|
||||||
|
declUnpackedArray(nodep, false);
|
||||||
|
} else {
|
||||||
|
newDeclFunc(nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void declPackedArray(AstPackArrayDType* const nodep, bool newFunc) {
|
||||||
|
string prefixName(newFunc ? "name" : m_traName);
|
||||||
|
|
||||||
VL_RESTORER(m_traName);
|
VL_RESTORER(m_traName);
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
addToSubFunc(new AstTracePushPrefix{flp, m_traName, VTracePrefixType::ARRAY_PACKED});
|
addToSubFunc(
|
||||||
|
new AstTracePushPrefix{flp, prefixName, VTracePrefixType::ARRAY_PACKED, !newFunc});
|
||||||
|
|
||||||
AstNodeDType* const subtypep = nodep->subDTypep()->skipRefToEnump();
|
AstNodeDType* const subtypep = nodep->subDTypep()->skipRefToEnump();
|
||||||
for (int i = nodep->lo(); i <= nodep->hi(); ++i) {
|
for (int i = nodep->lo(); i <= nodep->hi(); ++i) {
|
||||||
|
|
@ -588,23 +703,32 @@ class TraceDeclVisitor final : public VNVisitor {
|
||||||
|
|
||||||
addToSubFunc(new AstTracePopPrefix{flp});
|
addToSubFunc(new AstTracePopPrefix{flp});
|
||||||
}
|
}
|
||||||
void visit(AstStructDType* nodep) override {
|
void visit(AstPackArrayDType* nodep) override {
|
||||||
if (!m_traVscp) return;
|
if (!m_traVscp) return;
|
||||||
|
|
||||||
if (nodep->packed() && !v3Global.opt.traceStructs()) {
|
if (!v3Global.opt.traceStructs()) {
|
||||||
// Everything downstream is packed, so deal with as one trace unit
|
// Everything downstream is packed, so deal with as one trace unit.
|
||||||
// This may not be the nicest for user presentation, but is
|
// This may not be the nicest for user presentation, but is
|
||||||
// a much faster way to trace
|
// a much faster way to trace
|
||||||
addTraceDecl(VNumRange{}, nodep->width());
|
addTraceDecl(VNumRange{}, nodep->width());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VL_RESTORER(m_traName);
|
VL_RESTORER(m_skipDtypeFunc);
|
||||||
FileLine* const flp = nodep->fileline();
|
if (isBasicIO()) m_skipDtypeFunc = true;
|
||||||
|
|
||||||
|
if (m_skipDtypeFunc || m_dtypeFunc) {
|
||||||
|
declPackedArray(nodep, false);
|
||||||
|
} else {
|
||||||
|
newDeclFunc(nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void declStruct(AstStructDType* const nodep, bool newFunc) {
|
||||||
|
FileLine* const flp = nodep->fileline();
|
||||||
|
string prefixName(newFunc ? "name" : m_traName);
|
||||||
if (!nodep->packed()) {
|
if (!nodep->packed()) {
|
||||||
addToSubFunc(
|
addToSubFunc(new AstTracePushPrefix{flp, prefixName, VTracePrefixType::STRUCT_UNPACKED,
|
||||||
new AstTracePushPrefix{flp, m_traName, VTracePrefixType::STRUCT_UNPACKED});
|
!newFunc});
|
||||||
for (const AstMemberDType *itemp = nodep->membersp(), *nextp; itemp; itemp = nextp) {
|
for (const AstMemberDType *itemp = nodep->membersp(), *nextp; itemp; itemp = nextp) {
|
||||||
nextp = VN_AS(itemp->nextp(), MemberDType);
|
nextp = VN_AS(itemp->nextp(), MemberDType);
|
||||||
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefToEnump();
|
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefToEnump();
|
||||||
|
|
@ -618,7 +742,8 @@ class TraceDeclVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
addToSubFunc(new AstTracePopPrefix{flp});
|
addToSubFunc(new AstTracePopPrefix{flp});
|
||||||
} else {
|
} else {
|
||||||
addToSubFunc(new AstTracePushPrefix{flp, m_traName, VTracePrefixType::STRUCT_PACKED});
|
addToSubFunc(new AstTracePushPrefix{flp, prefixName, VTracePrefixType::STRUCT_PACKED,
|
||||||
|
!newFunc});
|
||||||
for (const AstMemberDType *itemp = nodep->membersp(), *nextp; itemp; itemp = nextp) {
|
for (const AstMemberDType *itemp = nodep->membersp(), *nextp; itemp; itemp = nextp) {
|
||||||
nextp = VN_AS(itemp->nextp(), MemberDType);
|
nextp = VN_AS(itemp->nextp(), MemberDType);
|
||||||
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefToEnump();
|
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefToEnump();
|
||||||
|
|
@ -633,9 +758,36 @@ class TraceDeclVisitor final : public VNVisitor {
|
||||||
addToSubFunc(new AstTracePopPrefix{flp});
|
addToSubFunc(new AstTracePopPrefix{flp});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void visit(AstStructDType* nodep) override {
|
||||||
|
if (!m_traVscp) return;
|
||||||
|
|
||||||
|
if (nodep->packed() && !v3Global.opt.traceStructs()) {
|
||||||
|
// Everything downstream is packed, so deal with as one trace unit
|
||||||
|
// This may not be the nicest for user presentation, but is
|
||||||
|
// a much faster way to trace
|
||||||
|
addTraceDecl(VNumRange{}, nodep->width());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VL_RESTORER(m_skipDtypeFunc);
|
||||||
|
if (isBasicIO()) m_skipDtypeFunc = true;
|
||||||
|
|
||||||
|
// Only create sub functions for top-level structs, i.e. don't have struct funcs
|
||||||
|
// call other struct funcs for child types. This could easily be done for decl funcs
|
||||||
|
// but full / chg funcs would require copying / aligning data for child types or more
|
||||||
|
// complicated / wonky / generalized data access.
|
||||||
|
if (m_skipDtypeFunc || m_dtypeFunc) {
|
||||||
|
declStruct(nodep, false);
|
||||||
|
} else {
|
||||||
|
newDeclFunc(nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
void visit(AstUnionDType* nodep) override {
|
void visit(AstUnionDType* nodep) override {
|
||||||
if (!m_traVscp) return;
|
if (!m_traVscp) return;
|
||||||
|
|
||||||
|
VL_RESTORER(m_skipDtypeFunc);
|
||||||
|
m_skipDtypeFunc = true;
|
||||||
|
|
||||||
if (nodep->packed() && !v3Global.opt.traceStructs()) {
|
if (nodep->packed() && !v3Global.opt.traceStructs()) {
|
||||||
// Everything downstream is packed, so deal with as one trace unit
|
// Everything downstream is packed, so deal with as one trace unit
|
||||||
// This may not be the nicest for user presentation, but is
|
// This may not be the nicest for user presentation, but is
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_clas
|
||||||
test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_2")
|
test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_2")
|
||||||
|
|
||||||
# Check combine count
|
# Check combine count
|
||||||
test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (272 if test.vltmt else 255))
|
test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (276 if test.vltmt else 257))
|
||||||
test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_FAST + (\d+)', 2)
|
test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_FAST + (\d+)', 2)
|
||||||
test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_SLOW + (\d+)', 2)
|
test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_SLOW + (\d+)', 2)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ test.compile(verilator_flags2=["--trace-vcd", "--trace-structs", "--output-split
|
||||||
|
|
||||||
if test.vlt_all:
|
if test.vlt_all:
|
||||||
test.file_grep_count(test.obj_dir + "/V" + test.name + "__Trace__0.cpp",
|
test.file_grep_count(test.obj_dir + "/V" + test.name + "__Trace__0.cpp",
|
||||||
r'void Vt.*trace_chg_.*sub.*{', 3)
|
r'void Vt.*trace_chg_.*sub.*{', 3 if test.vltmt else 1)
|
||||||
|
|
||||||
test.execute()
|
test.execute()
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2024 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
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios("simulator_st")
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=["--trace"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.vcd_identical(test.trace_filename, test.golden_filename)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2025 by Wilson Snyder.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`ifndef LAST_CYC
|
||||||
|
`define LAST_CYC 9
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifndef NUM_SUBS
|
||||||
|
`define NUM_SUBS 4
|
||||||
|
`endif
|
||||||
|
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
int cyc;
|
||||||
|
logic [`NUM_SUBS - 1:0] x;
|
||||||
|
initial cyc = 0;
|
||||||
|
|
||||||
|
always_ff @(posedge clk) begin
|
||||||
|
cyc <= cyc + 1;
|
||||||
|
if (cyc == `LAST_CYC) begin
|
||||||
|
if (~|x) $stop;
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for (genvar i = 0; i < `NUM_SUBS; i++) begin : gen_loop
|
||||||
|
int loop_cyc;
|
||||||
|
always_comb loop_cyc = cyc + i;
|
||||||
|
sub #(
|
||||||
|
.data_t (pkg::some_struct_t)
|
||||||
|
)
|
||||||
|
the_sub (
|
||||||
|
.a (loop_cyc[i%32]),
|
||||||
|
.b (loop_cyc[(i+1)%32]),
|
||||||
|
.x (x[i]),
|
||||||
|
.out_2d_unpacked (),
|
||||||
|
.data (),
|
||||||
|
.cyc (loop_cyc),
|
||||||
|
.clk
|
||||||
|
);
|
||||||
|
end
|
||||||
|
|
||||||
|
intf
|
||||||
|
the_intf_a (.*),
|
||||||
|
the_intf_b (.*);
|
||||||
|
|
||||||
|
for (genvar m = 0; m < 4; m++) begin : gen_intf_loop
|
||||||
|
always_comb begin
|
||||||
|
the_intf_a.data[m] = cyc[7:0] + m + 7'd1;
|
||||||
|
the_intf_b.data[m] = cyc[7:0] + m + 7'd2;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
package pkg;
|
||||||
|
typedef struct packed {
|
||||||
|
logic field_a;
|
||||||
|
logic [5:0] field_b;
|
||||||
|
logic [9:0] field_c;
|
||||||
|
} some_sub_struct_t;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
logic foo;
|
||||||
|
logic [3:0] [31:0] bar;
|
||||||
|
logic [15:0] baz;
|
||||||
|
logic [127:0] qux;
|
||||||
|
some_sub_struct_t sub_struct;
|
||||||
|
} some_struct_t;
|
||||||
|
|
||||||
|
parameter some_sub_struct_t SUB_ONES = '1;
|
||||||
|
parameter some_sub_struct_t SUB_ZEROS = '0;
|
||||||
|
endpackage
|
||||||
|
|
||||||
|
module sub #(
|
||||||
|
parameter type data_t = bit
|
||||||
|
)(
|
||||||
|
input a,
|
||||||
|
input b,
|
||||||
|
output logic x,
|
||||||
|
output out_2d_unpacked [3][4],
|
||||||
|
output data_t data,
|
||||||
|
input int cyc,
|
||||||
|
input clk
|
||||||
|
);
|
||||||
|
pkg::some_struct_t the_struct;
|
||||||
|
pkg::some_struct_t the_structs [3:0];
|
||||||
|
pkg::some_struct_t [2:0] the_packed_structs;
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
logic abc;
|
||||||
|
logic def;
|
||||||
|
logic xyz;
|
||||||
|
} some_struct_t;
|
||||||
|
some_struct_t the_local_struct;
|
||||||
|
localparam some_struct_t const_struct = 3'b101;
|
||||||
|
typedef some_struct_t typedefed_struct_t;
|
||||||
|
typedefed_struct_t the_typedefed_struct;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
logic field_a;
|
||||||
|
logic field_b;
|
||||||
|
logic field_c;
|
||||||
|
} some_unpacked_struct_t;
|
||||||
|
some_unpacked_struct_t the_local_unpacked_struct;
|
||||||
|
|
||||||
|
typedef union packed {
|
||||||
|
struct packed {
|
||||||
|
logic [7:0] field_0;
|
||||||
|
} union_a;
|
||||||
|
struct packed {
|
||||||
|
logic [3:0] field_1;
|
||||||
|
logic [3:0] field_2;
|
||||||
|
} union_b;
|
||||||
|
struct packed {
|
||||||
|
logic [1:0] field_3;
|
||||||
|
logic [5:0] field_4;
|
||||||
|
} union_c;
|
||||||
|
} some_union_t;
|
||||||
|
some_union_t the_local_union;
|
||||||
|
|
||||||
|
typedef logic [1:0] [31:0] logic_array_t;
|
||||||
|
typedef logic [1:0] [31:0] logic_array_2_t;
|
||||||
|
logic_array_t the_logic_array;
|
||||||
|
logic_array_2_t the_other_logic_array;
|
||||||
|
logic [15:0] the_unpacked_array [5];
|
||||||
|
logic the_2d_unpacked [3][4];
|
||||||
|
|
||||||
|
typedef logic [3:0] four_bit_t;
|
||||||
|
typedef four_bit_t [1:0] two_fours_t;
|
||||||
|
localparam two_fours_t two_fours = 8'hab;
|
||||||
|
two_fours_t two_fours_var;
|
||||||
|
|
||||||
|
always_ff @(posedge clk) begin
|
||||||
|
x <= a ^ b;
|
||||||
|
the_struct <= '{
|
||||||
|
foo : cyc[0],
|
||||||
|
bar : '{cyc, cyc+1, cyc+2, cyc+3},
|
||||||
|
baz : cyc[15:0],
|
||||||
|
qux : 128'(cyc),
|
||||||
|
sub_struct : '{
|
||||||
|
field_a : cyc[0],
|
||||||
|
field_b : cyc[5:0],
|
||||||
|
field_c : cyc[9:0]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (int i = 0; i < 4; i++) the_structs[i] <= {$bits(pkg::some_struct_t){cyc[i]}};
|
||||||
|
the_local_struct <= cyc[2:0];
|
||||||
|
the_typedefed_struct <= cyc[3:1];
|
||||||
|
the_local_unpacked_struct <= '{
|
||||||
|
field_a : cyc[0],
|
||||||
|
field_b : cyc[1],
|
||||||
|
field_c : cyc[2]
|
||||||
|
};
|
||||||
|
the_local_union <= cyc[7:0];
|
||||||
|
for (int i = 0; i < 2; i++) begin
|
||||||
|
the_logic_array[i] <= cyc + i;
|
||||||
|
the_other_logic_array[i] <= cyc + i + 123;
|
||||||
|
end
|
||||||
|
for (int i = 0; i < 5; i++) the_unpacked_array[i] <= cyc[15:0];
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
for (int j = 0; j < 4; j++) begin
|
||||||
|
the_2d_unpacked [i][j] <= ~(cyc[i] ^ cyc[j]);
|
||||||
|
out_2d_unpacked [i][j] <= cyc[i] ^ cyc[j];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always_comb data = the_struct;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
interface intf
|
||||||
|
(input wire clk);
|
||||||
|
logic [3:0] [7:0] data;
|
||||||
|
int data_typed;
|
||||||
|
always_comb data_typed = data;
|
||||||
|
endinterface
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2024 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
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios("simulator_st")
|
||||||
|
test.top_filename = "t/t_trace_type_dupes.v"
|
||||||
|
test.sim_time = 2000000 # NOCOMMIT -- for benchmarking, leave in?
|
||||||
|
|
||||||
|
test.compile(
|
||||||
|
# artificially low trace splitting for force cross-split type function usage
|
||||||
|
verilator_flags2=["--trace", "--trace-structs", "--output-split-ctrace 10"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.vcd_identical(test.trace_filename, test.golden_filename)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
Loading…
Reference in New Issue