Support $stacktrace
This commit is contained in:
parent
96306b7630
commit
e8a1e4745c
|
|
@ -554,3 +554,14 @@ or "`ifdef`"'s may break other tools.
|
||||||
|
|
||||||
Re-enable waveform tracing for all future signals or instances that are
|
Re-enable waveform tracing for all future signals or instances that are
|
||||||
declared.
|
declared.
|
||||||
|
|
||||||
|
.. option:: $stacktrace
|
||||||
|
|
||||||
|
Called as a task, orint a stack trace. Called as a function, return a
|
||||||
|
string with a stack trace. This relies on the C++ system trace, which
|
||||||
|
may give less meaningful results if the model was not compiled with
|
||||||
|
debug symbols. Also the data represents the C++ stack, the
|
||||||
|
SystemVerilog functions/tasks involved may be renamed and/or inlined
|
||||||
|
before becoming the C++ functions that may be visible in the stack
|
||||||
|
trace. This extension is experimental and may be removed without
|
||||||
|
deprecation.
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,10 @@
|
||||||
#if defined(_WIN32) || defined(__MINGW32__)
|
#if defined(_WIN32) || defined(__MINGW32__)
|
||||||
# include <direct.h> // mkdir
|
# include <direct.h> // mkdir
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef __linux__
|
||||||
|
# include <execinfo.h>
|
||||||
|
# define _VL_HAVE_STACKTRACE
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "verilated_threads.h"
|
#include "verilated_threads.h"
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
@ -1623,6 +1627,33 @@ IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi
|
||||||
return read_count;
|
return read_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string VL_STACKTRACE_N() VL_MT_SAFE {
|
||||||
|
static VerilatedMutex s_stackTraceMutex;
|
||||||
|
const VerilatedLockGuard lock{s_stackTraceMutex};
|
||||||
|
|
||||||
|
constexpr int BT_BUF_SIZE = 100;
|
||||||
|
void *buffer[BT_BUF_SIZE];
|
||||||
|
int nptrs = 0;
|
||||||
|
char ** strings = nullptr;
|
||||||
|
|
||||||
|
#ifdef _VL_HAVE_STACKTRACE
|
||||||
|
nptrs = backtrace(buffer, BT_BUF_SIZE);
|
||||||
|
strings = backtrace_symbols(buffer, nptrs);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!strings) return "Unable to backtrace\n";
|
||||||
|
|
||||||
|
std::string out = "Backtrace:\n";
|
||||||
|
for (int j = 0; j < nptrs; j++) out += std::string{strings[j]} + std::string{"\n"};
|
||||||
|
free(strings);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VL_STACKTRACE() VL_MT_SAFE {
|
||||||
|
const std::string out = VL_STACKTRACE_N();
|
||||||
|
VL_PRINTF("%s", out.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
|
IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
|
||||||
VlWide<VL_WQ_WORDS_E> lhsw;
|
VlWide<VL_WQ_WORDS_E> lhsw;
|
||||||
VL_SET_WQ(lhsw, lhs);
|
VL_SET_WQ(lhsw, lhs);
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,8 @@ extern void VL_SFORMAT_X(int obits, IData& destr, const char* formatp, ...);
|
||||||
extern void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...);
|
extern void VL_SFORMAT_X(int obits, QData& destr, const char* formatp, ...);
|
||||||
extern void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...);
|
extern void VL_SFORMAT_X(int obits, void* destp, const char* formatp, ...);
|
||||||
|
|
||||||
|
extern void VL_STACKTRACE() VL_MT_SAFE;
|
||||||
|
extern std::string VL_STACKTRACE_N() VL_MT_SAFE;
|
||||||
extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp);
|
extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp);
|
||||||
extern IData VL_SYSTEM_IQ(QData lhs);
|
extern IData VL_SYSTEM_IQ(QData lhs);
|
||||||
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
|
inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
|
||||||
|
|
|
||||||
|
|
@ -1525,6 +1525,25 @@ public:
|
||||||
int instrCount() const override { return widthInstrs(); }
|
int instrCount() const override { return widthInstrs(); }
|
||||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||||
};
|
};
|
||||||
|
class AstStackTraceF final : public AstNodeExpr {
|
||||||
|
// $stacktrace used as function
|
||||||
|
public:
|
||||||
|
AstStackTraceF(FileLine* fl)
|
||||||
|
: ASTGEN_SUPER_StackTraceF(fl) {
|
||||||
|
dtypeSetString();
|
||||||
|
}
|
||||||
|
ASTGEN_MEMBERS_AstStackTraceF;
|
||||||
|
string verilogKwd() const override { return "$stacktrace"; }
|
||||||
|
string emitVerilog() override { return verilogKwd(); }
|
||||||
|
string emitC() override { return "VL_STACKTRACE_N()"; }
|
||||||
|
bool isGateOptimizable() const override { return false; }
|
||||||
|
bool isPredictOptimizable() const override { return false; }
|
||||||
|
bool isPure() const override { return false; }
|
||||||
|
bool isOutputter() const override { return true; }
|
||||||
|
bool isUnlikely() const override { return true; }
|
||||||
|
bool cleanOut() const override { return true; }
|
||||||
|
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||||
|
};
|
||||||
class AstSysIgnore final : public AstNodeExpr {
|
class AstSysIgnore final : public AstNodeExpr {
|
||||||
// @astgen op1 := exprsp : List[AstNode] // Expressions to output (???)
|
// @astgen op1 := exprsp : List[AstNode] // Expressions to output (???)
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -3092,6 +3092,20 @@ public:
|
||||||
int instrCount() const override { return INSTR_COUNT_PLI; }
|
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||||
};
|
};
|
||||||
|
class AstStackTraceT final : public AstNodeStmt {
|
||||||
|
// $stacktrace used as task
|
||||||
|
public:
|
||||||
|
AstStackTraceT(FileLine* fl)
|
||||||
|
: ASTGEN_SUPER_StackTraceT(fl) { }
|
||||||
|
ASTGEN_MEMBERS_AstStackTraceT;
|
||||||
|
string verilogKwd() const override { return "$stacktrace"; }
|
||||||
|
bool isGateOptimizable() const override { return false; }
|
||||||
|
bool isPredictOptimizable() const override { return false; }
|
||||||
|
bool isPure() const override { return false; }
|
||||||
|
bool isOutputter() const override { return true; }
|
||||||
|
bool isUnlikely() const override { return true; }
|
||||||
|
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||||
|
};
|
||||||
class AstStmtExpr final : public AstNodeStmt {
|
class AstStmtExpr final : public AstNodeStmt {
|
||||||
// Expression in statement position
|
// Expression in statement position
|
||||||
// @astgen op1 := exprp : AstNodeExpr
|
// @astgen op1 := exprp : AstNodeExpr
|
||||||
|
|
|
||||||
|
|
@ -775,6 +775,12 @@ public:
|
||||||
iterateAndNextNull(nodep->lhsp());
|
iterateAndNextNull(nodep->lhsp());
|
||||||
if (!nodep->lhsp()->isWide()) puts(";");
|
if (!nodep->lhsp()->isWide()) puts(";");
|
||||||
}
|
}
|
||||||
|
void visit(AstStackTraceF* nodep) override {
|
||||||
|
puts("VL_STACKTRACE_N()");
|
||||||
|
}
|
||||||
|
void visit(AstStackTraceT* nodep) override {
|
||||||
|
puts("VL_STACKTRACE();\n");
|
||||||
|
}
|
||||||
void visit(AstSystemT* nodep) override {
|
void visit(AstSystemT* nodep) override {
|
||||||
puts("(void)VL_SYSTEM_I");
|
puts("(void)VL_SYSTEM_I");
|
||||||
emitIQW(nodep->lhsp());
|
emitIQW(nodep->lhsp());
|
||||||
|
|
|
||||||
|
|
@ -4676,6 +4676,9 @@ private:
|
||||||
userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p());
|
userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void visit(AstStackTraceF* nodep) override {
|
||||||
|
nodep->dtypeSetString();
|
||||||
|
}
|
||||||
void visit(AstSysIgnore* nodep) override {
|
void visit(AstSysIgnore* nodep) override {
|
||||||
userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p());
|
userIterateAndNext(nodep->exprsp(), WidthVP(SELF, BOTH).p());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -265,6 +265,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
||||||
"$skew" { FL; return yaTIMINGSPEC; }
|
"$skew" { FL; return yaTIMINGSPEC; }
|
||||||
"$sqrt" { FL; return yD_SQRT; }
|
"$sqrt" { FL; return yD_SQRT; }
|
||||||
"$sscanf" { FL; return yD_SSCANF; }
|
"$sscanf" { FL; return yD_SSCANF; }
|
||||||
|
"$stacktrace" { FL; return yD_STACKTRACE; }
|
||||||
"$stime" { FL; return yD_STIME; }
|
"$stime" { FL; return yD_STIME; }
|
||||||
"$stop" { FL; return yD_STOP; }
|
"$stop" { FL; return yD_STOP; }
|
||||||
"$strobe" { FL; return yD_STROBE; }
|
"$strobe" { FL; return yD_STROBE; }
|
||||||
|
|
|
||||||
|
|
@ -860,6 +860,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
|
||||||
%token<fl> yD_SQRT "$sqrt"
|
%token<fl> yD_SQRT "$sqrt"
|
||||||
%token<fl> yD_SSCANF "$sscanf"
|
%token<fl> yD_SSCANF "$sscanf"
|
||||||
%token<fl> yD_STABLE "$stable"
|
%token<fl> yD_STABLE "$stable"
|
||||||
|
%token<fl> yD_STACKTRACE "$stacktrace"
|
||||||
%token<fl> yD_STIME "$stime"
|
%token<fl> yD_STIME "$stime"
|
||||||
%token<fl> yD_STOP "$stop"
|
%token<fl> yD_STOP "$stop"
|
||||||
%token<fl> yD_STROBE "$strobe"
|
%token<fl> yD_STROBE "$strobe"
|
||||||
|
|
@ -3788,6 +3789,7 @@ system_t_call<nodep>: // IEEE: system_tf_call (as task)
|
||||||
| yD_DUMPON '(' expr ')' { $$ = new AstDumpCtl($<fl>1, VDumpCtlType::ON); DEL($3); }
|
| yD_DUMPON '(' expr ')' { $$ = new AstDumpCtl($<fl>1, VDumpCtlType::ON); DEL($3); }
|
||||||
//
|
//
|
||||||
| yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? nullptr : new AstUCStmt($1,$3)); }
|
| yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? nullptr : new AstUCStmt($1,$3)); }
|
||||||
|
| yD_STACKTRACE parenE { $$ = new AstStackTraceT{$1}; }
|
||||||
| yD_SYSTEM '(' expr ')' { $$ = new AstSystemT($1, $3); }
|
| yD_SYSTEM '(' expr ')' { $$ = new AstSystemT($1, $3); }
|
||||||
//
|
//
|
||||||
| yD_EXIT parenE { $$ = new AstFinish($1); }
|
| yD_EXIT parenE { $$ = new AstFinish($1); }
|
||||||
|
|
@ -3897,6 +3899,7 @@ system_f_call<nodeExprp>: // IEEE: system_tf_call (as func)
|
||||||
//
|
//
|
||||||
| yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? nullptr : new AstUCFunc($1,$3)); }
|
| yD_C '(' cStrList ')' { $$ = (v3Global.opt.ignc() ? nullptr : new AstUCFunc($1,$3)); }
|
||||||
| yD_CAST '(' expr ',' expr ')' { $$ = new AstCastDynamic($1, $5, $3); }
|
| yD_CAST '(' expr ',' expr ')' { $$ = new AstCastDynamic($1, $5, $3); }
|
||||||
|
| yD_STACKTRACE parenE { $$ = new AstStackTraceF{$1}; }
|
||||||
| yD_SYSTEM '(' expr ')' { $$ = new AstSystemF($1,$3); }
|
| yD_SYSTEM '(' expr ')' { $$ = new AstSystemF($1,$3); }
|
||||||
//
|
//
|
||||||
| system_f_call_or_t { $$ = $1; }
|
| system_f_call_or_t { $$ = $1; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2003 by Wilson Snyder. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
compile(
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under The Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2022 by Wilson Snyder.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t;
|
||||||
|
|
||||||
|
task t;
|
||||||
|
// verilator no_inline_task
|
||||||
|
string trace;
|
||||||
|
|
||||||
|
$display("== Trace Func");
|
||||||
|
trace = $stacktrace();
|
||||||
|
if (trace == "") $stop;
|
||||||
|
$display("%s", trace);
|
||||||
|
|
||||||
|
$display("== Trace Task");
|
||||||
|
$stacktrace;
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
initial t();
|
||||||
|
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue