Optimize trace code for faster compiles on repeated types (#6707)
This commit is contained in:
parent
446bec3d1a
commit
e4bcdc4f4e
|
|
@ -1071,6 +1071,16 @@ public:
|
|||
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 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 {
|
||||
return lhs.m_e == rhs.m_e;
|
||||
|
|
|
|||
|
|
@ -4451,6 +4451,7 @@ public:
|
|||
string selfPointerProtect(bool useSelfForThis) const {
|
||||
return selfPointer().protect(useSelfForThis, protect());
|
||||
}
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
};
|
||||
class AstCMethodCall final : public AstNodeCCall {
|
||||
// C++ method call
|
||||
|
|
|
|||
|
|
@ -2043,6 +2043,10 @@ public:
|
|||
}
|
||||
VDirection direction() const VL_MT_SAFE { return m_direction; }
|
||||
bool isIO() const VL_MT_SAFE { return m_direction != VDirection::NONE; }
|
||||
bool isVLIO() const {
|
||||
const AstBasicDType* const bdtypep = basicp();
|
||||
return isPrimaryIO() && bdtypep && !bdtypep->isOpaque();
|
||||
}
|
||||
void declDirection(const VDirection& flag) { m_declDirection = flag; }
|
||||
VDirection declDirection() const { return m_declDirection; }
|
||||
void varType(VVarType type) { m_varType = type; }
|
||||
|
|
@ -2061,7 +2065,7 @@ public:
|
|||
string dpiTmpVarType(const string& varName) const;
|
||||
// Return Verilator internal type for argument: CData, SData, IData, WData
|
||||
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 vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
|
||||
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
|
||||
|
|
|
|||
|
|
@ -1180,6 +1180,9 @@ class AstTraceDecl final : public AstNodeStmt {
|
|||
// Parents: {statement list}
|
||||
// Expression being traced - Moved to AstTraceInc by V3Trace
|
||||
// @astgen op1 := valuep : Optional[AstNodeExpr]
|
||||
//
|
||||
// @astgen ptr := m_dtypeCallp: Optional[AstCCall] // Type init function call
|
||||
// @astgen ptr := m_dtypeDeclp: Optional[AstTraceDecl] // CCall TraceDecl which replaces this
|
||||
uint32_t m_code{std::numeric_limits<uint32_t>::max()}; // Trace identifier code
|
||||
uint32_t m_fidx{0}; // Trace function index
|
||||
const string m_showname; // Name of variable
|
||||
|
|
@ -1187,18 +1190,23 @@ class AstTraceDecl final : public AstNodeStmt {
|
|||
const VNumRange m_arrayRange; // Property of var the trace details
|
||||
const VVarType m_varType; // Type of variable (for localparam vs. param)
|
||||
const VDirection m_declDirection; // Declared direction input/output etc
|
||||
const bool m_inDtypeFunc; // Trace decl inside type init function
|
||||
int m_codeInc{0}; // Code increment for type
|
||||
public:
|
||||
AstTraceDecl(FileLine* fl, const string& showname,
|
||||
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, const bool inDtypeFunc)
|
||||
: ASTGEN_SUPER_TraceDecl(fl)
|
||||
, m_showname{showname}
|
||||
, m_bitRange{bitRange}
|
||||
, m_arrayRange{arrayRange}
|
||||
, m_varType{varp->varType()}
|
||||
, m_declDirection{varp->declDirection()} {
|
||||
, m_declDirection{varp->declDirection()}
|
||||
, m_inDtypeFunc{inDtypeFunc} {
|
||||
dtypeFrom(valuep);
|
||||
this->valuep(valuep);
|
||||
this->dtypeCallp(dtypeCallp);
|
||||
}
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
|
|
@ -1207,7 +1215,7 @@ public:
|
|||
string name() const override VL_MT_STABLE { return m_showname; }
|
||||
bool maybePointedTo() 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
|
||||
// Details on what we're tracing
|
||||
uint32_t code() const { return m_code; }
|
||||
|
|
@ -1215,7 +1223,9 @@ public:
|
|||
bool codeAssigned() const { return m_code != std::numeric_limits<uint32_t>::max(); }
|
||||
uint32_t fidx() const { return m_fidx; }
|
||||
void fidx(uint32_t fidx) { m_fidx = fidx; }
|
||||
void codeInc(uint32_t codeInc) { m_codeInc = codeInc; }
|
||||
uint32_t codeInc() const {
|
||||
if (m_codeInc) { return m_codeInc; }
|
||||
return (m_arrayRange.ranged() ? m_arrayRange.elements() : 1)
|
||||
* valuep()->dtypep()->widthWords()
|
||||
* (VL_EDATASIZE / 32); // A code is always 32-bits
|
||||
|
|
@ -1224,6 +1234,11 @@ public:
|
|||
const VNumRange& arrayRange() const { return m_arrayRange; }
|
||||
VVarType varType() const { return m_varType; }
|
||||
VDirection declDirection() const { return m_declDirection; }
|
||||
AstCCall* dtypeCallp() const { return m_dtypeCallp; }
|
||||
void dtypeCallp(AstCCall* const callp) { m_dtypeCallp = callp; }
|
||||
AstTraceDecl* dtypeDeclp() const { return m_dtypeDeclp; }
|
||||
void dtypeDeclp(AstTraceDecl* const declp) { m_dtypeDeclp = declp; }
|
||||
bool inDtypeFunc() const { return m_inDtypeFunc; }
|
||||
};
|
||||
class AstTraceInc final : public AstNodeStmt {
|
||||
// Trace point dump
|
||||
|
|
@ -1270,15 +1285,19 @@ public:
|
|||
class AstTracePushPrefix final : public AstNodeStmt {
|
||||
const string m_prefix; // Prefix to add to signal names
|
||||
const VTracePrefixType m_prefixType; // Type of prefix being pushed
|
||||
const bool m_quotedPrefix; // Quote prefix name
|
||||
public:
|
||||
AstTracePushPrefix(FileLine* fl, const string& prefix, VTracePrefixType prefixType)
|
||||
AstTracePushPrefix(FileLine* fl, const string& prefix, VTracePrefixType prefixType,
|
||||
bool quotedPrefix = true)
|
||||
: ASTGEN_SUPER_TracePushPrefix(fl)
|
||||
, m_prefix{prefix}
|
||||
, m_prefixType{prefixType} {}
|
||||
, m_prefixType{prefixType}
|
||||
, m_quotedPrefix{quotedPrefix} {}
|
||||
ASTGEN_MEMBERS_AstTracePushPrefix;
|
||||
bool sameNode(const AstNode* samep) const override { return false; }
|
||||
string prefix() const { return m_prefix; }
|
||||
VTracePrefixType prefixType() const { return m_prefixType; }
|
||||
bool quotedPrefix() const { return m_quotedPrefix; }
|
||||
};
|
||||
class AstWait final : public AstNodeStmt {
|
||||
// @astgen op1 := condp : AstNodeExpr
|
||||
|
|
|
|||
|
|
@ -614,7 +614,7 @@ string AstVar::verilogKwd() const {
|
|||
}
|
||||
|
||||
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc,
|
||||
bool asRef) const {
|
||||
bool asRef, bool constRef) const {
|
||||
UASSERT_OBJ(!forReturn, this,
|
||||
"Internal data is never passed as return, but as first argument");
|
||||
string ostatic;
|
||||
|
|
@ -622,7 +622,7 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
|
|||
|
||||
asRef = asRef || isDpiOpenArray() || (forFunc && (isWritable() || isRef() || isConstRef()));
|
||||
|
||||
if (forFunc && isReadOnly() && asRef) ostatic = ostatic + "const ";
|
||||
if (forFunc && (isReadOnly() || constRef) && asRef) ostatic = ostatic + "const ";
|
||||
|
||||
string oname;
|
||||
if (named) {
|
||||
|
|
@ -3166,7 +3166,8 @@ void AstStop::dumpJson(std::ostream& str) const {
|
|||
}
|
||||
void AstTraceDecl::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
if (code()) str << " [code=" << code() << "]";
|
||||
if (codeAssigned()) str << " [code=" << code() << "]";
|
||||
if (dtypeCallp()) str << " [dtypeCallp=" << dtypeCallp() << "]";
|
||||
}
|
||||
void AstTraceDecl::dumpJson(std::ostream& str) const {
|
||||
dumpJsonNumFunc(str, code);
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) {
|
|||
if (asRef && refNeedParens) puts(")");
|
||||
emitDeclArrayBrackets(nodep);
|
||||
puts(";\n");
|
||||
} else if (nodep->isPrimaryIO() && basicp && !basicp->isOpaque()) {
|
||||
} else if (nodep->isVLIO()) {
|
||||
if (nodep->isInout()) {
|
||||
putns(nodep, "VL_INOUT");
|
||||
} else if (nodep->isWritable()) {
|
||||
|
|
|
|||
|
|
@ -626,6 +626,16 @@ class EmitCTrace final : public EmitCFunc {
|
|||
}
|
||||
|
||||
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()) + ", " + direction);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodep->dtypep()->basicp()->isDouble()) {
|
||||
puts("tracep->declDouble(");
|
||||
} else if (nodep->isWide()) {
|
||||
|
|
@ -646,7 +656,11 @@ class EmitCTrace final : public EmitCFunc {
|
|||
|
||||
// Function index
|
||||
puts(",");
|
||||
puts(cvtToStr(nodep->fidx()));
|
||||
if (nodep->inDtypeFunc()) {
|
||||
puts("fidx");
|
||||
} else {
|
||||
puts(cvtToStr(nodep->fidx()));
|
||||
}
|
||||
|
||||
// Name
|
||||
puts(",");
|
||||
|
|
@ -656,14 +670,10 @@ class EmitCTrace final : public EmitCFunc {
|
|||
puts("," + cvtToStr(enumNum));
|
||||
|
||||
// Direction
|
||||
if (nodep->declDirection().isInout()) {
|
||||
puts(", VerilatedTraceSigDirection::INOUT");
|
||||
} else if (nodep->declDirection().isWritable()) {
|
||||
puts(", VerilatedTraceSigDirection::OUTPUT");
|
||||
} else if (nodep->declDirection().isNonOutput()) {
|
||||
puts(", VerilatedTraceSigDirection::INPUT");
|
||||
if (nodep->inDtypeFunc()) {
|
||||
puts(", direction");
|
||||
} else {
|
||||
puts(", VerilatedTraceSigDirection::NONE");
|
||||
puts(", " + direction);
|
||||
}
|
||||
|
||||
// Kind
|
||||
|
|
@ -754,16 +764,7 @@ class EmitCTrace final : public EmitCFunc {
|
|||
puts("VL_SC_BV_DATAP(");
|
||||
}
|
||||
iterateConst(varrefp); // Put var name out
|
||||
// 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) + "]");
|
||||
}
|
||||
}
|
||||
emitTraceIndex(nodep, arrayindex);
|
||||
if (varp->isSc()) puts(".read()");
|
||||
if (emitTraceIsScUint(nodep)) {
|
||||
puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()");
|
||||
|
|
@ -776,10 +777,24 @@ class EmitCTrace final : public EmitCFunc {
|
|||
} else {
|
||||
puts("(");
|
||||
iterateConst(nodep->valuep());
|
||||
emitTraceIndex(nodep, arrayindex);
|
||||
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
|
||||
using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning
|
||||
void visit(AstCFunc* nodep) override {
|
||||
|
|
@ -799,7 +814,11 @@ class EmitCTrace final : public EmitCFunc {
|
|||
}
|
||||
void visit(AstTracePushPrefix* nodep) override {
|
||||
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(nodep->prefixType().ascii());
|
||||
puts(");\n");
|
||||
|
|
|
|||
|
|
@ -429,11 +429,11 @@ class GateOkVisitor final : public VNVisitorConst {
|
|||
|
||||
// We only allow a LHS ref for the var being set, and a RHS ref for
|
||||
// something else being read.
|
||||
AstVarScope* const vscp = nodep->varScopep();
|
||||
if (nodep->access().isWriteOnly()) {
|
||||
if (m_lhsVarRef) clearSimple(">1 write refs");
|
||||
m_lhsVarRef = nodep;
|
||||
} else {
|
||||
AstVarScope* const vscp = nodep->varScopep();
|
||||
// TODO: possible bug, should it be >= 1 as add is below?
|
||||
if (m_readVscps.size() > 1) {
|
||||
if (m_buffersOnly) clearSimple(">1 rhs varRefs");
|
||||
|
|
|
|||
253
src/V3Trace.cpp
253
src/V3Trace.cpp
|
|
@ -43,9 +43,11 @@
|
|||
#include "V3EmitCBase.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
|
|
@ -162,8 +164,10 @@ class TraceVisitor final : public VNVisitor {
|
|||
// Cleared entire netlist
|
||||
// AstCFunc::user1() // V3GraphVertex* for this node
|
||||
// AstTraceDecl::user1() // V3GraphVertex* for this node
|
||||
// AstTraceDecl::user2() // dtype decl cannot be used for _chg
|
||||
// AstVarScope::user1() // V3GraphVertex* for this node
|
||||
// AstStmtExpr::user2() // bool; walked next list for other ccalls
|
||||
// AstVarRef::user2() // dtype V3TraceDecl* for this node
|
||||
// Ast*::user3() // TraceActivityVertex* for this node
|
||||
const VNUser1InUse m_inuser1;
|
||||
const VNUser2InUse m_inuser2;
|
||||
|
|
@ -183,6 +187,15 @@ class TraceVisitor final : public VNVisitor {
|
|||
V3Graph m_graph; // Var/CFunc tracking
|
||||
TraceActivityVertex* const m_alwaysVtxp; // "Always trace" vertex
|
||||
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.
|
||||
const uint32_t m_parallelism
|
||||
|
|
@ -206,13 +219,13 @@ class TraceVisitor final : public VNVisitor {
|
|||
// Hash all of the traced values and find if there are any duplicates
|
||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
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");
|
||||
const auto dupit = dupFinder.findDuplicate(nodep->valuep());
|
||||
const auto dupit = dupFinder.findDuplicate(nodep);
|
||||
if (dupit == dupFinder.end()) {
|
||||
dupFinder.insert(nodep->valuep());
|
||||
dupFinder.insert(nodep);
|
||||
} 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");
|
||||
TraceTraceVertex* const dupvertexp
|
||||
= dupDeclp->user1u().toGraphVertex()->cast<TraceTraceVertex>();
|
||||
|
|
@ -228,6 +241,43 @@ class TraceVisitor final : public VNVisitor {
|
|||
dupFinder.dumpFile(v3Global.debugFilename("trace") + ".hash", false);
|
||||
}
|
||||
|
||||
void graphDtypePrune() {
|
||||
for (V3GraphVertex* const vtxp : m_graph.vertices().unlinkable()) {
|
||||
if (TraceTraceVertex* const vvertexp = vtxp->cast<TraceTraceVertex>()) {
|
||||
AstTraceDecl* const declp = vvertexp->nodep();
|
||||
// This skips the dtype sub-func optimization if a var is affected by multiple
|
||||
// activities. We really only need to do this for _chg funcs (and not decls,
|
||||
// _const and _full) but it's simpiler to do it all one way or the other.
|
||||
if (declp) {
|
||||
if (declp->user2() || (declp->dtypeDeclp() && !declp->dtypeDeclp()->user2())) {
|
||||
AstCCall* const callp = declp->dtypeCallp();
|
||||
if (callp) {
|
||||
AstNode* stmtexprp = callp->backp();
|
||||
VL_DO_DANGLING(pushDeletep(stmtexprp->unlinkFrBack()), stmtexprp);
|
||||
}
|
||||
bool emptyScope;
|
||||
do {
|
||||
emptyScope = false;
|
||||
AstNode* const declBackp = declp->backp();
|
||||
AstNode* const declNextp = declp->nextp();
|
||||
if (VN_IS(declBackp, TracePushPrefix)
|
||||
&& VN_IS(declNextp, TracePopPrefix)) {
|
||||
VL_DO_DANGLING(pushDeletep(declBackp->unlinkFrBack()), declBackp);
|
||||
VL_DO_DANGLING(pushDeletep(declNextp->unlinkFrBack()), declNextp);
|
||||
emptyScope = true;
|
||||
}
|
||||
} while (emptyScope);
|
||||
// Can't purge until we finish this pass
|
||||
pushDeletep(declp->unlinkFrBack());
|
||||
vvertexp->rerouteEdges(&m_graph);
|
||||
vvertexp->unlinkDelete(&m_graph);
|
||||
}
|
||||
declp->dtypeDeclp(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void graphSimplify(bool initial) {
|
||||
if (initial) {
|
||||
// Remove all variable nodes
|
||||
|
|
@ -483,11 +533,11 @@ class TraceVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
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
|
||||
const bool isTopFunc = topFuncp == nullptr;
|
||||
const bool isTopFunc = !declp && topFuncp == nullptr;
|
||||
std::string funcName;
|
||||
if (isTopFunc) {
|
||||
if (isTopFunc || declp) {
|
||||
if (traceType == VTraceType::CONSTANT) {
|
||||
funcName = "trace_const";
|
||||
} else if (traceType == VTraceType::FULL) {
|
||||
|
|
@ -499,8 +549,13 @@ class TraceVisitor final : public VNVisitor {
|
|||
funcName = topFuncp->name();
|
||||
funcName += "_sub";
|
||||
}
|
||||
funcName += "_";
|
||||
funcName += cvtToStr(funcNum);
|
||||
if (declp) {
|
||||
funcName += "_dtype__";
|
||||
funcName = m_dtypeNames.get(funcName);
|
||||
} else {
|
||||
funcName += "_";
|
||||
funcName += cvtToStr(funcNum);
|
||||
}
|
||||
|
||||
FileLine* const flp = m_topScopep->fileline();
|
||||
AstCFunc* const funcp = new AstCFunc{flp, funcName, m_topScopep};
|
||||
|
|
@ -513,7 +568,10 @@ class TraceVisitor final : public VNVisitor {
|
|||
m_topScopep->addBlocksp(funcp);
|
||||
const std::string bufArg
|
||||
= v3Global.opt.traceClassBase()
|
||||
+ "::" + (v3Global.opt.useTraceOffload() ? "OffloadBuffer" : "Buffer") + "* bufp";
|
||||
+ "::" + (v3Global.opt.useTraceOffload() ? "OffloadBuffer" : "Buffer") + "* bufp"
|
||||
+ (declp ? (", uint32_t offset, const " + declp->dtypep()->cType("", true, true)
|
||||
+ " __VdtypeVar")
|
||||
: "");
|
||||
if (isTopFunc) {
|
||||
// Top functions
|
||||
funcp->argTypes("void* voidSelf, " + bufArg);
|
||||
|
|
@ -548,35 +606,62 @@ class TraceVisitor final : public VNVisitor {
|
|||
if (traceType != VTraceType::CHANGE) {
|
||||
// Full dump sub function
|
||||
funcp->addStmtsp(new AstCStmt{flp, //
|
||||
"uint32_t* const oldp VL_ATTR_UNUSED = "
|
||||
"bufp->oldp(vlSymsp->__Vm_baseCode);\n"});
|
||||
string("uint32_t* const oldp VL_ATTR_UNUSED = "
|
||||
"bufp->oldp(vlSymsp->__Vm_baseCode")
|
||||
+ (declp ? " + offset" : "") + ");\n"});
|
||||
} else {
|
||||
// Change dump sub function
|
||||
if (v3Global.opt.useTraceOffload()) {
|
||||
funcp->addStmtsp(new AstCStmt{flp, //
|
||||
"const uint32_t base VL_ATTR_UNUSED = "
|
||||
"vlSymsp->__Vm_baseCode + "
|
||||
+ cvtToStr(baseCode) + ";\n"});
|
||||
+ (declp ? " offset" : cvtToStr(baseCode))
|
||||
+ ";\n"});
|
||||
funcp->addStmtsp(
|
||||
new AstCStmt{flp, "(void)bufp; // Prevent unused variable warning\n"});
|
||||
} else {
|
||||
funcp->addStmtsp(new AstCStmt{flp, //
|
||||
"uint32_t* const oldp VL_ATTR_UNUSED = "
|
||||
"bufp->oldp(vlSymsp->__Vm_baseCode + "
|
||||
+ cvtToStr(baseCode) + ");\n"});
|
||||
+ (declp ? " offset" : cvtToStr(baseCode))
|
||||
+ ");\n"});
|
||||
}
|
||||
}
|
||||
// Add call to top function
|
||||
AstCCall* const callp = new AstCCall{funcp->fileline(), funcp};
|
||||
callp->dtypeSetVoid();
|
||||
callp->argTypes("bufp");
|
||||
topFuncp->addStmtsp(callp->makeStmt());
|
||||
if (!declp) {
|
||||
// Add call to top function
|
||||
AstCCall* const callp = new AstCCall{funcp->fileline(), funcp};
|
||||
callp->dtypeSetVoid();
|
||||
callp->argTypes("bufp");
|
||||
topFuncp->addStmtsp(callp->makeStmt());
|
||||
}
|
||||
}
|
||||
// Done
|
||||
UINFO(5, " newCFunc " << 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) {
|
||||
const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace()
|
||||
: std::numeric_limits<int>::max();
|
||||
|
|
@ -618,13 +703,51 @@ class TraceVisitor final : public VNVisitor {
|
|||
++subFuncNum;
|
||||
}
|
||||
FileLine* const flp = declp->fileline();
|
||||
AstTraceInc* const incp = new AstTraceInc{flp, declp, VTraceType::CONSTANT};
|
||||
subFuncp->addStmtsp(incp);
|
||||
subStmts += incp->nodeCount();
|
||||
if (declp->dtypeCallp()) {
|
||||
AstCFunc* const funcp = createConstDtypeTraceFunctions(declp);
|
||||
AstNodeExpr* argsp = nullptr;
|
||||
argsp = AstNode::addNext(argsp, declp->valuep()->cloneTree(false));
|
||||
AstCCall* const callp = new AstCCall{flp, funcp, argsp};
|
||||
callp->dtypeSetVoid();
|
||||
callp->argTypes(callp->argTypes() + "bufp, " + std::to_string(declp->code()));
|
||||
subFuncp->addStmtsp(callp->makeStmt());
|
||||
|
||||
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,
|
||||
uint32_t parallelism) {
|
||||
const int splitLimit = v3Global.opt.outputSplitCTrace() ? v3Global.opt.outputSplitCTrace()
|
||||
|
|
@ -700,24 +823,44 @@ class TraceVisitor final : public VNVisitor {
|
|||
|
||||
// Add TraceInc nodes
|
||||
FileLine* const flp = declp->fileline();
|
||||
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);
|
||||
if (declp->dtypeCallp()) {
|
||||
DtypeFuncs funcs = createNonConstDtypeTraceFunctions(declp);
|
||||
AstNodeExpr* argsp = nullptr;
|
||||
argsp = AstNode::addNext(argsp, declp->valuep()->cloneTree(false));
|
||||
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, declp->valuep()->cloneTree(false));
|
||||
AstCCall* const callChgp = new AstCCall{flp, funcs.chgFuncp, argsp};
|
||||
callChgp->dtypeSetVoid();
|
||||
callChgp->argTypes(callChgp->argTypes() + "bufp, "
|
||||
+ std::to_string(declp->code()));
|
||||
ifp->addThensp(callChgp->makeStmt());
|
||||
|
||||
// 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;
|
||||
subStmts += 2;
|
||||
} 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
|
||||
|
|
@ -761,6 +904,8 @@ class TraceVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
void createTraceFunctions() {
|
||||
graphDtypePrune();
|
||||
|
||||
// Detect and remove duplicate values
|
||||
detectDuplicates();
|
||||
m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
|
||||
|
|
@ -884,7 +1029,7 @@ class TraceVisitor final : public VNVisitor {
|
|||
|| nodep->isCoroutine()) {
|
||||
// Cannot treat a coroutine as slow, it may be resumed later
|
||||
const bool slow = nodep->slow() && !nodep->isCoroutine();
|
||||
V3GraphVertex* const activityVtxp = getActivityVertexp(nodep, slow);
|
||||
TraceActivityVertex* const activityVtxp = getActivityVertexp(nodep, slow);
|
||||
new V3GraphEdge{&m_graph, activityVtxp, funcVtxp, 1};
|
||||
}
|
||||
}
|
||||
|
|
@ -894,7 +1039,7 @@ class TraceVisitor final : public VNVisitor {
|
|||
}
|
||||
void visit(AstTraceDecl* nodep) override {
|
||||
UINFO(8, " TRACE " << nodep);
|
||||
if (!m_finding) {
|
||||
if (!m_finding && !nodep->inDtypeFunc()) {
|
||||
V3GraphVertex* const vertexp = new TraceTraceVertex{&m_graph, nodep};
|
||||
nodep->user1p(vertexp);
|
||||
|
||||
|
|
@ -905,10 +1050,11 @@ class TraceVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
UASSERT_OBJ(nodep->varScopep(), nodep, "No var scope?");
|
||||
AstVarScope* const varscopep = nodep->varScopep();
|
||||
V3GraphVertex* varVtxp = varscopep->user1u().toGraphVertex();
|
||||
if (m_tracep) {
|
||||
UASSERT_OBJ(nodep->varScopep(), nodep, "No var scope?");
|
||||
UASSERT_OBJ(nodep->access().isReadOnly(), nodep, "Lvalue in trace? Should be const.");
|
||||
V3GraphVertex* varVtxp = nodep->varScopep()->user1u().toGraphVertex();
|
||||
if (!varVtxp) {
|
||||
varVtxp = new TraceVarVertex{&m_graph, nodep->varScopep()};
|
||||
nodep->varScopep()->user1p(varVtxp);
|
||||
|
|
@ -919,12 +1065,23 @@ class TraceVisitor final : public VNVisitor {
|
|||
|| nodep->varp()->isSigPublic()) { // Or ones user can change
|
||||
new V3GraphEdge{&m_graph, m_alwaysVtxp, traceVtxp, 1};
|
||||
}
|
||||
if (m_tracep->dtypeCallp()) varscopep->user2p(m_tracep);
|
||||
} else if (m_cfuncp && m_finding && nodep->access().isWriteOrRW()) {
|
||||
UASSERT_OBJ(nodep->varScopep(), nodep, "No var scope?");
|
||||
V3GraphVertex* const funcVtxp = getCFuncVertexp(m_cfuncp);
|
||||
V3GraphVertex* const varVtxp = nodep->varScopep()->user1u().toGraphVertex();
|
||||
if (varVtxp) { // else we're not tracing this signal
|
||||
new V3GraphEdge{&m_graph, funcVtxp, varVtxp, 1};
|
||||
AstTraceDecl* const declp = VN_AS(varscopep->user2p(), TraceDecl);
|
||||
if (declp) {
|
||||
V3GraphVertex* const cFuncVtxp = getCFuncVertexp(m_cfuncp);
|
||||
for (const V3GraphEdge& edge : cFuncVtxp->inEdges()) {
|
||||
V3GraphVertex* const activityp = edge.fromp();
|
||||
if (!declp->user3p()) {
|
||||
declp->user3p(activityp);
|
||||
} else if (declp->user3u().toGraphVertex() != activityp) {
|
||||
declp->user2(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -935,7 +1092,15 @@ public:
|
|||
// CONSTRUCTORS
|
||||
explicit TraceVisitor(AstNetlist* nodep)
|
||||
: m_alwaysVtxp{new TraceActivityVertex{&m_graph, TraceActivityVertex::ACTIVITY_ALWAYS}} {
|
||||
nodep->user2ClearTree(); // TraceDecl multiple activities flag
|
||||
nodep->user3ClearTree(); // TraceDecl TraceActivityVertex (assumes we start at nullptr)
|
||||
iterate(nodep);
|
||||
nodep->foreach([](AstTraceDecl* const declp) {
|
||||
if (declp->inDtypeFunc()) {
|
||||
declp->valuep()->unlinkFrBack()->deleteTree();
|
||||
declp->valuep(nullptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
~TraceVisitor() override {
|
||||
V3Stats::addStat("Tracing, Activity setters", m_statSetters);
|
||||
|
|
|
|||
|
|
@ -26,11 +26,18 @@
|
|||
|
||||
#include "V3Control.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3File.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Number.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
|
@ -95,6 +102,8 @@ public:
|
|||
|
||||
class TraceDeclVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// AstCFunc::user1() // code offset for current type
|
||||
// AstCFunc::user2() // VarScope for dtype functions
|
||||
|
||||
// STATE
|
||||
AstTopScope* const m_topScopep; // The singleton AstTopScope
|
||||
|
|
@ -103,6 +112,12 @@ class TraceDeclVisitor final : public VNVisitor {
|
|||
std::vector<AstCFunc*> m_topFuncps; // Top level trace initialization functions
|
||||
std::vector<AstCFunc*> m_subFuncps; // Trace sub functions for this scope
|
||||
std::set<const AstTraceDecl*> m_declUncalledps; // Declarations not called
|
||||
std::unordered_map<const AstNodeDType*, AstCFunc*> m_dtypeFuncs; // Functions per type
|
||||
AstCFunc* m_dtypeFuncp = nullptr; // Current type func
|
||||
AstTraceDecl* m_dtypeDeclp = nullptr; // Current type func decl
|
||||
V3UniqueNames m_dtypeNames{""}; // Unique names for dtype funcs
|
||||
bool m_skipDtypeFunc = false; // Don't create a type func
|
||||
uint32_t m_offset = std::numeric_limits<uint32_t>::max(); // Offset for types
|
||||
int m_topFuncSize = 0; // Size of the top 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
|
||||
|
|
@ -234,6 +249,11 @@ class TraceDeclVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
void addToSubFunc(AstNodeStmt* stmtp) {
|
||||
// TODO (maybe) -- sub funcs for dtype components
|
||||
if (m_dtypeFuncp) {
|
||||
m_dtypeFuncp->addStmtsp(stmtp);
|
||||
return;
|
||||
}
|
||||
if (m_subFuncSize > m_funcSizeLimit || m_subFuncps.empty()) {
|
||||
m_subFuncSize = 0;
|
||||
//
|
||||
|
|
@ -248,20 +268,38 @@ class TraceDeclVisitor final : public VNVisitor {
|
|||
m_subFuncSize += stmtp->nodeCount();
|
||||
}
|
||||
|
||||
void addTraceDecl(const VNumRange& arrayRange,
|
||||
int widthOverride) { // If !=0, is packed struct/array where basicp size
|
||||
// misreflects one element
|
||||
AstTraceDecl* addTraceDecl(const VNumRange& arrayRange,
|
||||
int widthOverride, // If !=0, is packed struct/array where basicp
|
||||
// size misreflects one element
|
||||
AstCCall* const dtypeCallp = nullptr) {
|
||||
VNumRange bitRange;
|
||||
if (widthOverride) {
|
||||
bitRange = VNumRange{widthOverride - 1, 0};
|
||||
} else if (const AstBasicDType* const bdtypep = m_traValuep->dtypep()->basicp()) {
|
||||
bitRange = bdtypep->nrange();
|
||||
}
|
||||
FileLine* const flp = m_traVscp->fileline();
|
||||
AstNodeExpr* valuep = m_traValuep->cloneTree(false);
|
||||
const bool validOffset = m_offset != std::numeric_limits<uint32_t>::max();
|
||||
AstTraceDecl* const newp
|
||||
= new AstTraceDecl{m_traVscp->fileline(), m_traName, m_traVscp->varp(),
|
||||
m_traValuep->cloneTree(false), bitRange, arrayRange};
|
||||
= new AstTraceDecl{flp, m_traName, m_traVscp->varp(), valuep,
|
||||
bitRange, arrayRange, dtypeCallp, validOffset};
|
||||
if (validOffset) {
|
||||
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, "__VdtypeVar", m_traVscp->width()});
|
||||
VL_DO_DANGLING(refp->deleteTree(), refp);
|
||||
});
|
||||
} else {
|
||||
newp->dtypeDeclp(m_dtypeDeclp);
|
||||
}
|
||||
m_declUncalledps.emplace(newp);
|
||||
addToSubFunc(newp);
|
||||
|
||||
return newp;
|
||||
}
|
||||
|
||||
void addIgnore(const string& why) {
|
||||
|
|
@ -490,10 +528,14 @@ class TraceDeclVisitor final : public VNVisitor {
|
|||
// VISITORS - Data types when tracing
|
||||
void visit(AstConstDType* nodep) override {
|
||||
if (!m_traVscp) return;
|
||||
VL_RESTORER(m_offset);
|
||||
VL_RESTORER(m_skipDtypeFunc);
|
||||
m_skipDtypeFunc = true;
|
||||
iterate(nodep->subDTypep()->skipRefToEnump());
|
||||
}
|
||||
void visit(AstRefDType* nodep) override {
|
||||
if (!m_traVscp) return;
|
||||
VL_RESTORER(m_offset);
|
||||
iterate(nodep->subDTypep()->skipRefToEnump());
|
||||
}
|
||||
void visit(AstIfaceRefDType* nodep) override {
|
||||
|
|
@ -505,20 +547,58 @@ class TraceDeclVisitor final : public VNVisitor {
|
|||
addToSubFunc(stmtp);
|
||||
m_ifaceRefInitPlaceholders.emplace_back(m_traVscp, stmtp);
|
||||
}
|
||||
void visit(AstUnpackArrayDType* nodep) override {
|
||||
// Note more specific dtypes above
|
||||
if (!m_traVscp) return;
|
||||
void newDeclFunc(AstNodeDType* nodep) {
|
||||
AstNodeDType* const skipTypep = nodep->skipRefp();
|
||||
// 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()
|
||||
&& static_cast<int>(nodep->arrayUnpackedElements()) > v3Global.opt.traceMaxArray()) {
|
||||
addIgnore("Wide memory > --trace-max-array ents");
|
||||
return;
|
||||
auto pair = m_dtypeFuncs.emplace(skipTypep, nullptr);
|
||||
AstCFunc** funcpp = &pair.first->second;
|
||||
if (pair.second) {
|
||||
*funcpp = newCFunc(flp, m_dtypeNames.get("trace_init_dtype__"));
|
||||
(*funcpp)->user2p(m_traVscp);
|
||||
}
|
||||
|
||||
AstCCall* const callp = new AstCCall{flp, *funcpp};
|
||||
callp->dtypeSetVoid();
|
||||
callp->argTypes(callArgs);
|
||||
m_dtypeDeclp = addTraceDecl(VNumRange{}, skipTypep->width(), callp);
|
||||
addToSubFunc(callp->makeStmt());
|
||||
|
||||
if (pair.second) {
|
||||
VL_RESTORER(m_offset);
|
||||
m_offset = 0;
|
||||
|
||||
VL_RESTORER(m_dtypeFuncp);
|
||||
m_dtypeFuncp = *funcpp;
|
||||
m_dtypeFuncp->argTypes(m_dtypeFuncp->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");
|
||||
}
|
||||
m_dtypeFuncp->user1(m_offset);
|
||||
}
|
||||
|
||||
m_dtypeDeclp->codeInc((*funcpp)->user1());
|
||||
m_offset += m_dtypeDeclp->codeInc();
|
||||
}
|
||||
void declUnpackedArray(AstUnpackArrayDType* const nodep, bool newFunc) {
|
||||
string prefixName(newFunc ? "name" : m_traName);
|
||||
|
||||
VL_RESTORER(m_traName);
|
||||
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(),
|
||||
BasicDType) // Nothing lower than this array
|
||||
|
|
@ -546,20 +626,34 @@ class TraceDeclVisitor final : public VNVisitor {
|
|||
|
||||
addToSubFunc(new AstTracePopPrefix{flp});
|
||||
}
|
||||
void visit(AstPackArrayDType* nodep) override {
|
||||
bool isBasicIO() { return m_traVscp->varp()->isVLIO(); }
|
||||
void visit(AstUnpackArrayDType* nodep) override {
|
||||
// Note more specific dtypes above
|
||||
if (!m_traVscp) return;
|
||||
|
||||
if (!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());
|
||||
if (v3Global.opt.traceMaxArray()
|
||||
&& static_cast<int>(nodep->arrayUnpackedElements()) > v3Global.opt.traceMaxArray()) {
|
||||
addIgnore("Wide memory > --trace-max-array ents");
|
||||
return;
|
||||
}
|
||||
|
||||
VL_RESTORER(m_skipDtypeFunc);
|
||||
VL_RESTORER(m_dtypeDeclp);
|
||||
if (isBasicIO()) m_skipDtypeFunc = true;
|
||||
|
||||
if (!(m_skipDtypeFunc || m_dtypeDeclp)) {
|
||||
VL_RESTORER(m_offset);
|
||||
newDeclFunc(nodep);
|
||||
}
|
||||
declUnpackedArray(nodep, false);
|
||||
}
|
||||
void declPackedArray(AstPackArrayDType* const nodep, bool newFunc) {
|
||||
string prefixName(newFunc ? "name" : m_traName);
|
||||
|
||||
VL_RESTORER(m_traName);
|
||||
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();
|
||||
for (int i = nodep->lo(); i <= nodep->hi(); ++i) {
|
||||
|
|
@ -575,23 +669,33 @@ class TraceDeclVisitor final : public VNVisitor {
|
|||
|
||||
addToSubFunc(new AstTracePopPrefix{flp});
|
||||
}
|
||||
void visit(AstStructDType* nodep) override {
|
||||
void visit(AstPackArrayDType* nodep) override {
|
||||
if (!m_traVscp) return;
|
||||
|
||||
if (nodep->packed() && !v3Global.opt.traceStructs()) {
|
||||
// Everything downstream is packed, so deal with as one trace unit
|
||||
if (!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_traName);
|
||||
FileLine* const flp = nodep->fileline();
|
||||
VL_RESTORER(m_skipDtypeFunc);
|
||||
VL_RESTORER(m_dtypeDeclp);
|
||||
if (isBasicIO()) m_skipDtypeFunc = true;
|
||||
|
||||
if (!(m_skipDtypeFunc || m_dtypeDeclp)) {
|
||||
VL_RESTORER(m_offset);
|
||||
newDeclFunc(nodep);
|
||||
}
|
||||
declPackedArray(nodep, false);
|
||||
}
|
||||
void declStruct(AstStructDType* const nodep, bool newFunc) {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
string prefixName(newFunc ? "name" : m_traName);
|
||||
if (!nodep->packed()) {
|
||||
addToSubFunc(
|
||||
new AstTracePushPrefix{flp, m_traName, VTracePrefixType::STRUCT_UNPACKED});
|
||||
addToSubFunc(new AstTracePushPrefix{flp, prefixName, VTracePrefixType::STRUCT_UNPACKED,
|
||||
!newFunc});
|
||||
for (const AstMemberDType *itemp = nodep->membersp(), *nextp; itemp; itemp = nextp) {
|
||||
nextp = VN_AS(itemp->nextp(), MemberDType);
|
||||
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefToEnump();
|
||||
|
|
@ -605,7 +709,8 @@ class TraceDeclVisitor final : public VNVisitor {
|
|||
}
|
||||
addToSubFunc(new AstTracePopPrefix{flp});
|
||||
} 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) {
|
||||
nextp = VN_AS(itemp->nextp(), MemberDType);
|
||||
AstNodeDType* const subtypep = itemp->subDTypep()->skipRefToEnump();
|
||||
|
|
@ -620,9 +725,37 @@ class TraceDeclVisitor final : public VNVisitor {
|
|||
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);
|
||||
VL_RESTORER(m_dtypeDeclp);
|
||||
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_dtypeDeclp)) {
|
||||
VL_RESTORER(m_offset);
|
||||
newDeclFunc(nodep);
|
||||
}
|
||||
declStruct(nodep, false);
|
||||
}
|
||||
void visit(AstUnionDType* nodep) override {
|
||||
if (!m_traVscp) return;
|
||||
|
||||
VL_RESTORER(m_skipDtypeFunc);
|
||||
m_skipDtypeFunc = true;
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2677,7 +2677,7 @@ class VlTest:
|
|||
contents = self.file_contents(filename)
|
||||
if contents == "_Already_Errored_":
|
||||
return
|
||||
count = len(re.findall(regexp, contents))
|
||||
count = len(re.findall(regexp, contents, re.MULTILINE))
|
||||
if expcount != count:
|
||||
self.error("File_grep_count: " + filename + ": Got='" + str(count) + "' Expected='" +
|
||||
str(expcount) + "' in regexp: '" + regexp + "'")
|
||||
|
|
@ -2687,7 +2687,7 @@ class VlTest:
|
|||
contents = self.file_contents(filename)
|
||||
if contents == "_Already_Errored_":
|
||||
return
|
||||
match = re.search(regexp, contents)
|
||||
match = re.search(regexp, contents, re.MULTILINE)
|
||||
if match:
|
||||
if expvalue is not None and str(expvalue) != match.group(1):
|
||||
self.error("file_grep: " + filename + ": Got='" + match.group(1) +
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ module t;
|
|||
initial begin
|
||||
smaller = 8'hfa;
|
||||
bigger = bigger_t'(signed'(smaller));
|
||||
$display("%x", bigger); // NOCOMMIT
|
||||
if (bigger != 16'hfffa) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
# Check combine count
|
||||
test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (276 if test.vltmt else 259))
|
||||
test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (279 if test.vltmt else 262))
|
||||
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)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ test.compile(verilator_flags2=["--trace-vcd", "--trace-structs", "--output-split
|
|||
|
||||
if test.vlt_all:
|
||||
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()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
$version Generated by VerilatedVcd $end
|
||||
$timescale 1ps $end
|
||||
$scope module top $end
|
||||
$var wire 1 / clk $end
|
||||
$var wire 1 0 clk2 $end
|
||||
$scope module $unit $end
|
||||
$var wire 1 4 global_bit $end
|
||||
$upscope $end
|
||||
$scope module t $end
|
||||
$var wire 1 / clk $end
|
||||
$var wire 1 0 clk2 $end
|
||||
$var wire 32 1 cyc [31:0] $end
|
||||
$scope module v_strp $end
|
||||
$var wire 1 2 b1 $end
|
||||
$var wire 1 # b0 $end
|
||||
$upscope $end
|
||||
$scope module v_strp2 $end
|
||||
$var wire 1 $ b1 $end
|
||||
$var wire 1 % b0 $end
|
||||
$upscope $end
|
||||
$var wire 1 & foo $end
|
||||
$var wire 8 ' unpacked_array[-7] [7:0] $end
|
||||
$var wire 8 ( unpacked_array[-6] [7:0] $end
|
||||
$var wire 8 ) unpacked_array[-5] [7:0] $end
|
||||
$var wire 8 * unpacked_array[-4] [7:0] $end
|
||||
$var wire 8 + unpacked_array[-3] [7:0] $end
|
||||
$var wire 8 , unpacked_array[-2] [7:0] $end
|
||||
$var wire 8 - unpacked_array[-1] [7:0] $end
|
||||
$var wire 8 . unpacked_array[0] [7:0] $end
|
||||
$scope module unnamedblk1 $end
|
||||
$var wire 32 3 i [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$enddefinitions $end
|
||||
|
||||
|
||||
#0
|
||||
0#
|
||||
0$
|
||||
0%
|
||||
0&
|
||||
b00000000 '
|
||||
b00000000 (
|
||||
b00000000 )
|
||||
b00000000 *
|
||||
b00000000 +
|
||||
b00000000 ,
|
||||
b00000000 -
|
||||
b00000000 .
|
||||
0/
|
||||
00
|
||||
b00000000000000000000000000000000 1
|
||||
02
|
||||
b00000000000000000000000000000000 3
|
||||
04
|
||||
#10
|
||||
1$
|
||||
1%
|
||||
1&
|
||||
1/
|
||||
b00000000000000000000000000000001 1
|
||||
#15
|
||||
0/
|
||||
#20
|
||||
1#
|
||||
0$
|
||||
0%
|
||||
0&
|
||||
1/
|
||||
b00000000000000000000000000000010 1
|
||||
#25
|
||||
0/
|
||||
#30
|
||||
0#
|
||||
1$
|
||||
1%
|
||||
1&
|
||||
b00000001 .
|
||||
1/
|
||||
b00000000000000000000000000000011 1
|
||||
#35
|
||||
0/
|
||||
#40
|
||||
1#
|
||||
0$
|
||||
0%
|
||||
0&
|
||||
1/
|
||||
b00000000000000000000000000000100 1
|
||||
#45
|
||||
0/
|
||||
#50
|
||||
0#
|
||||
1$
|
||||
1%
|
||||
1&
|
||||
b00000010 .
|
||||
1/
|
||||
b00000000000000000000000000000101 1
|
||||
#55
|
||||
0/
|
||||
#60
|
||||
1#
|
||||
0$
|
||||
0%
|
||||
0&
|
||||
1/
|
||||
b00000000000000000000000000000110 1
|
||||
|
|
@ -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')
|
||||
|
||||
test.compile(verilator_flags2=['--cc --trace-vcd --trace-structs --no-trace-params'])
|
||||
|
||||
trace_cpp = test.obj_dir + "/" + test.vm_prefix + "__Trace__0.cpp"
|
||||
test.file_grep(trace_cpp, r"^ *Vt_.*trace_chg_dtype.*t__DOT__v_strp2")
|
||||
test.file_grep_count(trace_cpp, r"^ *Vt_.*trace_chg_dtype", 1)
|
||||
|
||||
test.execute()
|
||||
|
||||
test.vcd_identical(test.trace_filename, test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2009 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
bit global_bit;
|
||||
|
||||
module t (clk, clk2);
|
||||
input clk;
|
||||
input clk2;
|
||||
integer cyc = 0;
|
||||
|
||||
typedef struct packed {
|
||||
bit b1;
|
||||
bit b0;
|
||||
} strp_t;
|
||||
|
||||
strp_t v_strp, v_strp2;
|
||||
logic foo;
|
||||
|
||||
logic [7:0] unpacked_array[-7:0];
|
||||
|
||||
always @ (posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
foo <= ~foo;
|
||||
v_strp.b0 <= cyc[0];
|
||||
v_strp2 <= ~v_strp2;
|
||||
unpacked_array[0] = cyc[8:1];
|
||||
if (cyc == 5) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk2) begin
|
||||
v_strp.b1 <= cyc[1];
|
||||
for (int i = -1; i > -8; i--)
|
||||
unpacked_array[i] = cyc[7:0];
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
$version Generated by VerilatedVcd $end
|
||||
$timescale 1ps $end
|
||||
$scope module top $end
|
||||
$var wire 1 1 clk $end
|
||||
$var wire 1 2 clk2 $end
|
||||
$scope module $unit $end
|
||||
$var wire 1 4 global_bit $end
|
||||
$upscope $end
|
||||
$scope module t $end
|
||||
$var wire 1 1 clk $end
|
||||
$var wire 1 2 clk2 $end
|
||||
$var wire 32 3 cyc [31:0] $end
|
||||
$scope module v_strp $end
|
||||
$var wire 1 . b1 $end
|
||||
$var wire 1 # b0 $end
|
||||
$upscope $end
|
||||
$scope module v_strp2 $end
|
||||
$var wire 1 / b1 $end
|
||||
$var wire 1 $ b0 $end
|
||||
$upscope $end
|
||||
$var wire 1 % foo $end
|
||||
$var wire 8 & unpacked_array[-7] [7:0] $end
|
||||
$var wire 8 ' unpacked_array[-6] [7:0] $end
|
||||
$var wire 8 ( unpacked_array[-5] [7:0] $end
|
||||
$var wire 8 ) unpacked_array[-4] [7:0] $end
|
||||
$var wire 8 * unpacked_array[-3] [7:0] $end
|
||||
$var wire 8 + unpacked_array[-2] [7:0] $end
|
||||
$var wire 8 , unpacked_array[-1] [7:0] $end
|
||||
$var wire 8 - unpacked_array[0] [7:0] $end
|
||||
$scope module unnamedblk1 $end
|
||||
$var wire 32 0 i [31:0] $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$upscope $end
|
||||
$enddefinitions $end
|
||||
|
||||
|
||||
#0
|
||||
0#
|
||||
0$
|
||||
0%
|
||||
b00000000 &
|
||||
b00000000 '
|
||||
b00000000 (
|
||||
b00000000 )
|
||||
b00000000 *
|
||||
b00000000 +
|
||||
b00000000 ,
|
||||
b00000000 -
|
||||
0.
|
||||
0/
|
||||
b00000000000000000000000000000000 0
|
||||
01
|
||||
02
|
||||
b00000000000000000000000000000000 3
|
||||
04
|
||||
#10
|
||||
1%
|
||||
11
|
||||
b00000000000000000000000000000001 3
|
||||
#15
|
||||
01
|
||||
#20
|
||||
1#
|
||||
0%
|
||||
11
|
||||
b00000000000000000000000000000010 3
|
||||
#25
|
||||
01
|
||||
#30
|
||||
0#
|
||||
1%
|
||||
b00000001 -
|
||||
11
|
||||
b00000000000000000000000000000011 3
|
||||
#35
|
||||
01
|
||||
#40
|
||||
1#
|
||||
0%
|
||||
11
|
||||
b00000000000000000000000000000100 3
|
||||
#45
|
||||
01
|
||||
#50
|
||||
0#
|
||||
1$
|
||||
1%
|
||||
b00000010 -
|
||||
11
|
||||
b00000000000000000000000000000101 3
|
||||
#55
|
||||
01
|
||||
#60
|
||||
1#
|
||||
0%
|
||||
11
|
||||
b00000000000000000000000000000110 3
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
#!/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')
|
||||
|
||||
test.compile(verilator_flags2=['--cc --trace-vcd --trace-structs --no-trace-params'])
|
||||
|
||||
trace_cpp = test.obj_dir + "/" + test.vm_prefix + "__Trace__0.cpp"
|
||||
test.file_grep_count(trace_cpp, r"^ *Vt_.*trace_chg_dtype", 0)
|
||||
|
||||
test.execute()
|
||||
|
||||
test.vcd_identical(test.trace_filename, test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2009 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
bit global_bit;
|
||||
|
||||
module t (clk, clk2);
|
||||
input clk;
|
||||
input clk2;
|
||||
integer cyc = 0;
|
||||
|
||||
typedef struct packed {
|
||||
bit b1;
|
||||
bit b0;
|
||||
} strp_t;
|
||||
|
||||
strp_t v_strp, v_strp2;
|
||||
logic foo;
|
||||
|
||||
logic [7:0] unpacked_array[-7:0];
|
||||
|
||||
always @ (posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
foo <= ~foo;
|
||||
v_strp.b0 <= cyc[0];
|
||||
v_strp2.b0 <= cyc[2];
|
||||
unpacked_array[0] = cyc[8:1];
|
||||
if (cyc == 5) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk2) begin
|
||||
v_strp.b1 <= cyc[1];
|
||||
v_strp2.b1 <= cyc[3];
|
||||
for (int i = -1; i > -8; i--)
|
||||
unpacked_array[i] = cyc[7:0];
|
||||
end
|
||||
endmodule
|
||||
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,23 @@
|
|||
#!/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.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