Support tracing through --hierarchical/--lib-create libraries (#3200).

This commit is contained in:
Wilson Snyder 2021-11-27 17:07:27 -05:00
parent 833686446c
commit 04e0c7e4f1
10 changed files with 6654 additions and 29 deletions

View File

@ -14,6 +14,7 @@ Verilator 4.215 devel
**Major:**
* Add --lib-create, similar to --protect-lib but without protections.
* Support tracing through --hierarchical/--lib-create libraries (#3200).
**Minor:**

View File

@ -489,6 +489,13 @@ or "`ifdef`"'s may break other tools.
Verilator) text that should be passed through to the XML output as a tag,
for use by downstream applications.
.. option:: /*verilator&32;trace_init_task*/
Attached to a DPI import to indicate that function should be called when
initializing tracing. This attribute is indented only to be used
internally in code that Verilator generates when :vlopt:`--lib-create`
or :vlopt:`--hierarchical` is used along with :vlopt:`--trace`.
.. option:: /*verilator&32;tracing_off*/
Disable waveform tracing for all future signals that are declared in

View File

@ -2737,6 +2737,7 @@ private:
bool m_dpiContext : 1; // DPI import context
bool m_dpiOpenChild : 1; // DPI import open array child wrapper
bool m_dpiTask : 1; // DPI import task (vs. void function)
bool m_dpiTraceInit : 1; // DPI trace_init
bool m_isConstructor : 1; // Class constructor
bool m_isHideLocal : 1; // Verilog local
bool m_isHideProtected : 1; // Verilog protected
@ -2760,6 +2761,7 @@ protected:
, m_dpiContext{false}
, m_dpiOpenChild{false}
, m_dpiTask{false}
, m_dpiTraceInit{false}
, m_isConstructor{false}
, m_isHideLocal{false}
, m_isHideProtected{false}
@ -2822,6 +2824,8 @@ public:
bool dpiOpenChild() const { return m_dpiOpenChild; }
void dpiTask(bool flag) { m_dpiTask = flag; }
bool dpiTask() const { return m_dpiTask; }
void dpiTraceInit(bool flag) { m_dpiTraceInit = flag; }
bool dpiTraceInit() const { return m_dpiTraceInit; }
void isConstructor(bool flag) { m_isConstructor = flag; }
bool isConstructor() const { return m_isConstructor; }
bool isHideLocal() const { return m_isHideLocal; }

View File

@ -8781,11 +8781,12 @@ private:
bool m_isVirtual : 1; // Virtual function
bool m_entryPoint : 1; // User may call into this top level function
bool m_pure : 1; // Pure function
bool m_dpiContext : 1; // Declared as 'context' DPI import/export function
bool m_dpiExportDispatcher : 1; // This is the DPI export entry point (i.e.: called by user)
bool m_dpiExportImpl : 1; // DPI export implementation (called from DPI dispatcher via lookup)
bool m_dpiImportPrototype : 1; // This is the DPI import prototype (i.e.: provided by user)
bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code
bool m_dpiContext : 1; // Declared as 'context' DPI import/export function
bool m_dpiTraceInit : 1; // DPI trace_init
public:
AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "")
: ASTGEN_SUPER_CFunc(fl) {
@ -8808,11 +8809,12 @@ public:
m_isVirtual = false;
m_entryPoint = false;
m_pure = false;
m_dpiContext = false;
m_dpiExportDispatcher = false;
m_dpiExportImpl = false;
m_dpiImportPrototype = false;
m_dpiImportWrapper = false;
m_dpiContext = false;
m_dpiTraceInit = false;
}
ASTNODE_NODE_FUNCS(CFunc)
virtual string name() const override { return m_name; }
@ -8880,6 +8882,8 @@ public:
void entryPoint(bool flag) { m_entryPoint = flag; }
bool pure() const { return m_pure; }
void pure(bool flag) { m_pure = flag; }
bool dpiContext() const { return m_dpiContext; }
void dpiContext(bool flag) { m_dpiContext = flag; }
bool dpiExportDispatcher() const { return m_dpiExportDispatcher; }
void dpiExportDispatcher(bool flag) { m_dpiExportDispatcher = flag; }
bool dpiExportImpl() const { return m_dpiExportImpl; }
@ -8888,8 +8892,8 @@ public:
void dpiImportPrototype(bool flag) { m_dpiImportPrototype = flag; }
bool dpiImportWrapper() const { return m_dpiImportWrapper; }
void dpiImportWrapper(bool flag) { m_dpiImportWrapper = flag; }
bool dpiContext() const { return m_dpiContext; }
void dpiContext(bool flag) { m_dpiContext = flag; }
void dpiTraceInit(bool flag) { m_dpiTraceInit = flag; }
bool dpiTraceInit() const { return m_dpiTraceInit; }
//
// If adding node accessors, see below emptyBody
AstNode* argsp() const { return op1p(); }

View File

@ -23,14 +23,31 @@
#include "V3UniqueNames.h"
#include <algorithm>
#include <functional>
#include <vector>
class EmitCModel final : public EmitCFunc {
// TYPES
using CFuncVector = std::vector<const AstCFunc*>;
// MEMBERS
V3UniqueNames m_uniqueNames; // For generating unique file names
// METHODS
VL_DEBUG_FUNC;
CFuncVector findFuncps(std::function<bool(const AstCFunc*)> cb) {
CFuncVector funcps;
for (AstNode* nodep = m_modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstCFunc* const funcp = VN_CAST(nodep, CFunc)) {
if (cb(funcp)) funcps.push_back(funcp);
}
}
stable_sort(funcps.begin(), funcps.end(),
[](const AstNode* ap, const AstNode* bp) { return ap->name() < bp->name(); });
return funcps;
}
void putSectionDelimiter(const string& name) {
puts("\n");
puts("//============================================================\n");
@ -187,22 +204,11 @@ class EmitCModel final : public EmitCFunc {
// Emit DPI export dispatcher declarations
{
std::vector<const AstCFunc*> funcps;
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstCFunc* const funcp = VN_CAST(nodep, CFunc)) {
if (!funcp->dpiExportDispatcher()) continue;
funcps.push_back(funcp);
}
}
stable_sort(funcps.begin(), funcps.end(), [](const AstNode* ap, const AstNode* bp) {
return ap->name() < bp->name();
});
const CFuncVector funcps
= findFuncps([](const AstCFunc* nodep) { return nodep->dpiExportDispatcher(); });
if (!funcps.empty()) {
puts("\n/// DPI Export functions\n");
for (const AstCFunc* funcp : funcps) { emitCFuncDecl(funcp, modp); }
for (const AstCFunc* funcp : funcps) emitCFuncDecl(funcp, modp);
}
}
@ -568,12 +574,38 @@ class EmitCModel final : public EmitCFunc {
+ topModNameProtected + "* vlSelf, " + v3Global.opt.traceClassBase()
+ "* tracep);\n");
const CFuncVector traceInitFuncps
= findFuncps([](const AstCFunc* nodep) { return nodep->dpiTraceInit(); });
for (const AstCFunc* const funcp : traceInitFuncps) emitCFuncDecl(funcp, modp);
// ::trace
puts("\nVL_ATTR_COLD void " + topClassName() + "::trace(");
puts(v3Global.opt.traceClassBase() + "C* tfp, int, int) {\n");
puts(v3Global.opt.traceClassBase() + "C* tfp, int levels, int options) {\n");
puts(/**/ "if (false && levels && options) {} // Prevent unused\n");
puts(/**/ "tfp->spTrace()->addInitCb(&" + protect("trace_init") + ", &(vlSymsp->TOP));\n");
puts(/**/ topModNameProtected + "__" + protect("trace_register")
+ "(&(vlSymsp->TOP), tfp->spTrace());\n");
if (!traceInitFuncps.empty()) {
puts(/**/ "if (levels > 0) {\n");
puts(/****/ "const QData tfpq = reinterpret_cast<QData>(tfp);\n");
for (const AstCFunc* const funcp : traceInitFuncps) {
// Some hackery to locate handle__V for trace_init_task
// Considered a pragma on the handle, but that still doesn't help us attach it here
string handle = funcp->name();
const size_t wr_len = strlen("__Vdpiimwrap_");
UASSERT_OBJ(handle.substr(0, wr_len) == "__Vdpiimwrap_", funcp,
"Strange trace_init_task function name");
handle = "vlSymsp->TOP." + handle.substr(wr_len);
const string::size_type pos = handle.rfind("__DOT__");
UASSERT_OBJ(pos != string::npos, funcp, "Strange trace_init_task function name");
handle = handle.substr(0, pos) + "__DOT__handle___05FV";
puts(funcNameProtect(funcp, modp) + "(" + handle
+ ", tfpq, levels - 1, options);\n");
}
puts(/**/ "}\n");
}
puts("}\n");
}

View File

@ -124,6 +124,11 @@ private:
addComment(txtp, fl, "Evaluates the library module's final process");
}
void traceComment(AstTextBlock* txtp, FileLine* fl) {
addComment(txtp, fl, "Enables the library module's tracing");
addComment(txtp, fl, "Only usable when used with called from Verilator");
}
void createSvFile(FileLine* fl, AstNodeModule* modp) {
// Comments
AstTextBlock* const txtp = new AstTextBlock(fl);
@ -197,6 +202,18 @@ private:
txtp->addText(fl, "import \"DPI-C\" function void " + m_libName
+ "_protectlib_final(chandle handle__V);\n\n");
if (v3Global.opt.trace() && !v3Global.opt.protectIds()) {
txtp->addText(fl, "`ifdef verilator\n");
traceComment(txtp, fl);
txtp->addText(fl, "import \"DPI-C\" function void " + m_libName
+ "_protectlib_trace(chandle handle__V, "
"chandle tfp, int levels, int options)"
+ " /*verilator trace_init_task*/;\n");
// Note V3EmitCModel.cpp requires the name "handle__V".
txtp->addText(fl, "`endif // verilator\n");
txtp->addText(fl, "\n");
}
// Local variables
// Avoid tracing handle, as it is not a stable value, so breaks vcddiff
// Likewise other internals aren't interesting to the user
@ -387,6 +404,18 @@ private:
txtp->addText(fl, /**/ "delete handlep__V;\n");
txtp->addText(fl, "}\n\n");
if (v3Global.opt.trace() && !v3Global.opt.protectIds()) {
traceComment(txtp, fl);
txtp->addText(fl, "void " + m_libName
+ "_protectlib_trace(void* vhandlep__V, void* tfp, int levels, "
"int options) {\n");
castPtr(fl, txtp);
txtp->addText(
fl,
/**/ "handlep__V->trace(static_cast<VerilatedVcdC*>(tfp), levels, options);\n");
txtp->addText(fl, "}\n\n");
}
txtp->addText(fl, "}\n");
m_cfilep->tblockp(txtp);
}

View File

@ -933,8 +933,8 @@ private:
: nodep->dpiTask() ? "int"
: "";
AstCFunc* const funcp = new AstCFunc(nodep->fileline(), nodep->cname(), m_scopep, rtnType);
funcp->dpiImportPrototype(true);
funcp->dpiContext(nodep->dpiContext());
funcp->dpiImportPrototype(true);
funcp->dontCombine(true);
funcp->entryPoint(false);
funcp->isMethod(false);
@ -1201,9 +1201,10 @@ private:
cfuncp->dontCombine(!nodep->dpiImport());
cfuncp->entryPoint(!nodep->dpiImport());
cfuncp->funcPublic(nodep->taskPublic());
cfuncp->dpiContext(nodep->dpiContext());
cfuncp->dpiExportImpl(nodep->dpiExport());
cfuncp->dpiImportWrapper(nodep->dpiImport());
cfuncp->dpiContext(nodep->dpiContext());
cfuncp->dpiTraceInit(nodep->dpiTraceInit());
if (nodep->dpiImport() || nodep->dpiExport()) {
cfuncp->isStatic(true);
cfuncp->isLoose(true);

View File

@ -745,6 +745,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"/*verilator split_var*/" { FL; return yVL_SPLIT_VAR; }
"/*verilator tag"[^*]*"*/" { FL; yylval.strp = PARSEP->newString(V3ParseImp::lexParseTag(yytext));
return yVL_TAG; }
"/*verilator trace_init_task*/" { FL; return yVL_TRACE_INIT_TASK; }
"/*verilator tracing_off*/" { FL_FWD; PARSEP->lexFileline()->tracingOn(false); FL_BRK; }
"/*verilator tracing_on*/" { FL_FWD; PARSEP->lexFileline()->tracingOn(true); FL_BRK; }

View File

@ -829,6 +829,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
%token<fl> yVL_SFORMAT "/*verilator sformat*/"
%token<fl> yVL_SPLIT_VAR "/*verilator split_var*/"
%token<strp> yVL_TAG "/*verilator tag*/"
%token<fl> yVL_TRACE_INIT_TASK "/*verilator trace_init_task*/"
%token<fl> yP_TICK "'"
%token<fl> yP_TICKBRA "'{"
@ -4125,16 +4126,24 @@ array_methodWith<nodep>:
;
dpi_import_export<nodep>: // ==IEEE: dpi_import_export
yIMPORT yaSTRING dpi_tf_import_propertyE dpi_importLabelE function_prototype ';'
{ $$ = $5; if (*$4 != "") $5->cname(*$4);
$5->dpiContext($3==iprop_CONTEXT); $5->pure($3==iprop_PURE);
$5->dpiImport(true); GRAMMARP->checkDpiVer($1,*$2); v3Global.dpi(true);
yIMPORT yaSTRING dpi_tf_import_propertyE dpi_importLabelE function_prototype dpi_tf_TraceInitE ';'
{ $$ = $5;
if (*$4 != "") $5->cname(*$4);
$5->dpiContext($3 == iprop_CONTEXT);
$5->pure($3 == iprop_PURE);
$5->dpiImport(true);
$5->dpiTraceInit($6);
GRAMMARP->checkDpiVer($1, *$2); v3Global.dpi(true);
if ($$->prettyName()[0]=='$') SYMP->reinsert($$,nullptr,$$->prettyName()); // For $SysTF overriding
SYMP->reinsert($$); }
| yIMPORT yaSTRING dpi_tf_import_propertyE dpi_importLabelE task_prototype ';'
{ $$ = $5; if (*$4 != "") $5->cname(*$4);
$5->dpiContext($3==iprop_CONTEXT); $5->pure($3==iprop_PURE);
$5->dpiImport(true); $5->dpiTask(true); GRAMMARP->checkDpiVer($1,*$2); v3Global.dpi(true);
{ $$ = $5;
if (*$4 != "") $5->cname(*$4);
$5->dpiContext($3 == iprop_CONTEXT);
$5->pure($3 == iprop_PURE);
$5->dpiImport(true);
$5->dpiTask(true);
GRAMMARP->checkDpiVer($1, *$2); v3Global.dpi(true);
if ($$->prettyName()[0]=='$') SYMP->reinsert($$,nullptr,$$->prettyName()); // For $SysTF overriding
SYMP->reinsert($$); }
| yEXPORT yaSTRING dpi_importLabelE yFUNCTION idAny ';'
@ -4156,6 +4165,12 @@ dpi_tf_import_propertyE<iprop>: // IEEE: [ dpi_function_import_property + dpi_ta
| yPURE { $$ = iprop_PURE; }
;
dpi_tf_TraceInitE<cbool>: // Verilator extension
/* empty */ { $$ = false; }
| yVL_TRACE_INIT_TASK { $$ = true; $<fl>$ = $<fl>1; }
;
//************************************************
// Expressions
//

File diff suppressed because it is too large Load Diff