Support recursive constant functions.
This commit is contained in:
parent
712ff95a48
commit
b6ecffeb60
1
Changes
1
Changes
|
|
@ -43,6 +43,7 @@ Verilator 5.039 devel
|
||||||
* Support disabling a fork from within that fork (#6314). [Ryszard Rozak, Antmicro Ltd.]
|
* Support disabling a fork from within that fork (#6314). [Ryszard Rozak, Antmicro Ltd.]
|
||||||
* Support future sampled value functions.
|
* Support future sampled value functions.
|
||||||
* Support simple disable within task (#6334). [Ryszard Rozak, Antmicro Ltd.]
|
* Support simple disable within task (#6334). [Ryszard Rozak, Antmicro Ltd.]
|
||||||
|
* Support recursive constant functions.
|
||||||
* Change control file `public_flat_*` and other signal attributes to support __ in names (#6140).
|
* Change control file `public_flat_*` and other signal attributes to support __ in names (#6140).
|
||||||
* Change runtime to exit() instead of abort(), unless under +verilated+debug.
|
* Change runtime to exit() instead of abort(), unless under +verilated+debug.
|
||||||
* Change `$display("%p")` to remove space after `}`.
|
* Change `$display("%p")` to remove space after `}`.
|
||||||
|
|
|
||||||
114
src/V3Simulate.h
114
src/V3Simulate.h
|
|
@ -74,6 +74,10 @@ class SimulateVisitor VL_NOT_FINAL : public VNVisitorConst {
|
||||||
// is missing, we will not apply the optimization, rather then bomb.
|
// is missing, we will not apply the optimization, rather then bomb.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// CONSTANTS
|
||||||
|
static constexpr int CONST_FUNC_RECURSION_MAX = 1000;
|
||||||
|
static constexpr int CALL_STACK_MAX = 100;
|
||||||
|
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// Cleared on each always/assignw
|
// Cleared on each always/assignw
|
||||||
const VNUser1InUse m_inuser1;
|
const VNUser1InUse m_inuser1;
|
||||||
|
|
@ -136,12 +140,13 @@ private:
|
||||||
bool m_isCoverage; // Has coverage
|
bool m_isCoverage; // Has coverage
|
||||||
int m_instrCount; ///< Number of nodes
|
int m_instrCount; ///< Number of nodes
|
||||||
int m_dataCount; ///< Bytes of data
|
int m_dataCount; ///< Bytes of data
|
||||||
|
int m_recurseCount = 0; // Now deep in current recursion
|
||||||
AstJumpGo* m_jumpp = nullptr; ///< Jump label we're branching from
|
AstJumpGo* m_jumpp = nullptr; ///< Jump label we're branching from
|
||||||
// Simulating:
|
// Simulating:
|
||||||
// Allocators for constants of various data types
|
// Allocators for constants of various data types
|
||||||
std::unordered_map<const AstNodeDType*, ConstAllocator> m_constps;
|
std::unordered_map<const AstNodeDType*, ConstAllocator> m_constps;
|
||||||
size_t m_constGeneration = 0;
|
size_t m_constGeneration = 0;
|
||||||
std::vector<SimStackNode*> m_callStack; ///< Call stack for verbose error messages
|
std::vector<SimStackNode*> m_callStack; // Call stack for verbose error messages
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
// V3Numbers that represents strings are a bit special and the API for
|
// V3Numbers that represents strings are a bit special and the API for
|
||||||
|
|
@ -217,6 +222,7 @@ public:
|
||||||
} // LCOV_EXCL_STOP
|
} // LCOV_EXCL_STOP
|
||||||
m_whyNotOptimizable = why;
|
m_whyNotOptimizable = why;
|
||||||
std::ostringstream stack;
|
std::ostringstream stack;
|
||||||
|
int n = 0;
|
||||||
for (const auto& callstack : vlstd::reverse_view(m_callStack)) {
|
for (const auto& callstack : vlstd::reverse_view(m_callStack)) {
|
||||||
const AstFuncRef* const funcp = callstack->m_funcp;
|
const AstFuncRef* const funcp = callstack->m_funcp;
|
||||||
stack << "\n " << funcp->fileline() << "... Called from '"
|
stack << "\n " << funcp->fileline() << "... Called from '"
|
||||||
|
|
@ -232,6 +238,10 @@ public:
|
||||||
<< prettyNumber(&valp->num(), dtypep);
|
<< prettyNumber(&valp->num(), dtypep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (++n > CALL_STACK_MAX) {
|
||||||
|
stack << "\n ... stack truncated";
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_whyNotOptimizable += stack.str();
|
m_whyNotOptimizable += stack.str();
|
||||||
}
|
}
|
||||||
|
|
@ -444,6 +454,18 @@ private:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void initVar(AstVar* nodep) {
|
||||||
|
if (const AstBasicDType* const basicp = nodep->dtypeSkipRefp()->basicp()) {
|
||||||
|
AstConst cnst{nodep->fileline(), AstConst::WidthedValue{}, basicp->widthMin(), 0};
|
||||||
|
if (basicp->isZeroInit()) {
|
||||||
|
cnst.num().setAllBits0();
|
||||||
|
} else {
|
||||||
|
cnst.num().setAllBitsX();
|
||||||
|
}
|
||||||
|
newValue(nodep, &cnst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
void visit(AstAlways* nodep) override {
|
void visit(AstAlways* nodep) override {
|
||||||
if (jumpingOver()) return;
|
if (jumpingOver()) return;
|
||||||
|
|
@ -906,6 +928,8 @@ private:
|
||||||
|
|
||||||
iterateAndNextConstNull(nodep->rhsp()); // Value to assign
|
iterateAndNextConstNull(nodep->rhsp()); // Value to assign
|
||||||
handleAssignRecurse(nodep, nodep->lhsp(), nodep->rhsp());
|
handleAssignRecurse(nodep, nodep->lhsp(), nodep->rhsp());
|
||||||
|
// UINFO(9, "set " << fetchConst(nodep->rhsp())->num().ascii() << " for assign "
|
||||||
|
// << nodep->lhsp()->name());
|
||||||
}
|
}
|
||||||
void visit(AstArraySel* nodep) override {
|
void visit(AstArraySel* nodep) override {
|
||||||
checkNodeInfo(nodep);
|
checkNodeInfo(nodep);
|
||||||
|
|
@ -1143,17 +1167,49 @@ private:
|
||||||
VL_DANGLING(funcp); // Make sure we've sized the function
|
VL_DANGLING(funcp); // Make sure we've sized the function
|
||||||
funcp = nodep->taskp();
|
funcp = nodep->taskp();
|
||||||
UASSERT_OBJ(funcp, nodep, "Not linked");
|
UASSERT_OBJ(funcp, nodep, "Not linked");
|
||||||
|
|
||||||
if (funcp->recursive()) {
|
if (funcp->recursive()) {
|
||||||
// Because we attach values to nodes rather then making a stack, this is a mess
|
if (m_recurseCount >= CONST_FUNC_RECURSION_MAX) {
|
||||||
// When we do support this, we need a stack depth limit of 1K or something,
|
clearOptimizable(funcp, "Constant function recursed more than "s
|
||||||
// and the t_func_recurse_param_bad.v test should check that limit's error message
|
+ std::to_string(CONST_FUNC_RECURSION_MAX) + " times");
|
||||||
clearOptimizable(funcp, "Unsupported: Recursive constant functions");
|
return;
|
||||||
return;
|
}
|
||||||
|
++m_recurseCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values from previous call, so can save to stack
|
||||||
|
// The "stack" is this visit function's local stack, as this visit is itself recursing
|
||||||
|
std::map<AstNode*, AstNodeExpr*> oldValues;
|
||||||
|
|
||||||
|
if (funcp->recursive() && !m_checkOnly) {
|
||||||
|
// Save local automatics
|
||||||
|
funcp->foreach([this, &oldValues](AstVar* varp) {
|
||||||
|
if (varp->lifetime().isAutomatic()) { // This also does function's I/O
|
||||||
|
if (AstNodeExpr* const valuep = fetchValueNull(varp)) {
|
||||||
|
AstNodeExpr* const nvaluep = newTrackedClone(valuep);
|
||||||
|
oldValues.emplace(varp, nvaluep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Save every expression value, as might be in middle of expression
|
||||||
|
// that calls recursively back to this same function.
|
||||||
|
// This is much heavier-weight then likely is needed, in theory
|
||||||
|
// we could look at the visit stack to determine what nodes
|
||||||
|
// need save-restore, but that is difficult to get right, and
|
||||||
|
// recursion is rare.
|
||||||
|
funcp->foreach([this, &oldValues](AstNodeExpr* exprp) {
|
||||||
|
if (VN_IS(exprp, Const)) return; // Speed up as won't change
|
||||||
|
if (AstNodeExpr* const valuep = fetchValueNull(exprp)) {
|
||||||
|
AstNodeExpr* const nvaluep = newTrackedClone(valuep);
|
||||||
|
oldValues.emplace(exprp, nvaluep);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// Apply function call values to function
|
// Apply function call values to function
|
||||||
V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
|
V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
|
||||||
// Must do this in two steps, eval all params, then apply them
|
// Must do this in two steps, eval all params, then apply them
|
||||||
// Otherwise chained functions may have the wrong results
|
// Otherwise chained functions may have the wrong results
|
||||||
|
std::vector<std::pair<AstVar*, AstNodeExpr*>> portValues;
|
||||||
for (V3TaskConnects::iterator it = tconnects.begin(); it != tconnects.end(); ++it) {
|
for (V3TaskConnects::iterator it = tconnects.begin(); it != tconnects.end(); ++it) {
|
||||||
AstVar* const portp = it->first;
|
AstVar* const portp = it->first;
|
||||||
AstNode* const pinp = it->second->exprp();
|
AstNode* const pinp = it->second->exprp();
|
||||||
|
|
@ -1166,37 +1222,45 @@ private:
|
||||||
}
|
}
|
||||||
// Evaluate pin value
|
// Evaluate pin value
|
||||||
iterateConst(pinp);
|
iterateConst(pinp);
|
||||||
|
// Clone in case are recursing
|
||||||
|
portValues.push_back(std::make_pair(portp, newTrackedClone(fetchValue(pinp))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (V3TaskConnects::iterator it = tconnects.begin(); it != tconnects.end(); ++it) {
|
// Apply value to the function
|
||||||
AstVar* const portp = it->first;
|
if (!m_checkOnly && optimizable())
|
||||||
AstNode* const pinp = it->second->exprp();
|
for (auto& it : portValues) {
|
||||||
if (pinp) { // Else too few arguments in function call - ignore it
|
if (!m_checkOnly && optimizable()) newValue(it.first, it.second);
|
||||||
// Apply value to the function
|
|
||||||
if (!m_checkOnly && optimizable()) newValue(portp, fetchValue(pinp));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
SimStackNode stackNode{nodep, &tconnects};
|
SimStackNode stackNode{nodep, &tconnects};
|
||||||
// cppcheck-suppress danglingLifetime
|
// cppcheck-suppress danglingLifetime
|
||||||
m_callStack.push_back(&stackNode);
|
m_callStack.push_back(&stackNode);
|
||||||
// Clear output variable
|
if (!m_checkOnly) {
|
||||||
if (const auto* const basicp = VN_CAST(funcp->fvarp(), Var)->basicp()) {
|
// Clear output variable
|
||||||
AstConst cnst{funcp->fvarp()->fileline(), AstConst::WidthedValue{}, basicp->widthMin(),
|
initVar(VN_CAST(funcp->fvarp(), Var));
|
||||||
0};
|
// Clear other automatic variables
|
||||||
if (basicp->isZeroInit()) {
|
funcp->foreach([this, &oldValues](AstVar* varp) {
|
||||||
cnst.num().setAllBits0();
|
if (varp->lifetime().isAutomatic() && !varp->isIO()) initVar(varp);
|
||||||
} else {
|
});
|
||||||
cnst.num().setAllBitsX();
|
|
||||||
}
|
|
||||||
newValue(funcp->fvarp(), &cnst);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the function
|
// Evaluate the function
|
||||||
iterateConst(funcp);
|
iterateConst(funcp);
|
||||||
m_callStack.pop_back();
|
m_callStack.pop_back();
|
||||||
|
AstNodeExpr* returnp = nullptr;
|
||||||
if (!m_checkOnly && optimizable()) {
|
if (!m_checkOnly && optimizable()) {
|
||||||
// Grab return value from output variable (if it's a function)
|
// Grab return value from output variable
|
||||||
UASSERT_OBJ(funcp->fvarp(), nodep, "Function reference points at non-function");
|
UASSERT_OBJ(funcp->fvarp(), nodep, "Function reference points at non-function");
|
||||||
newValue(nodep, fetchValue(funcp->fvarp()));
|
returnp = newTrackedClone(fetchValue(funcp->fvarp()));
|
||||||
|
UINFO(5, "func " << nodep->name() << " return = " << returnp);
|
||||||
|
}
|
||||||
|
// Restore local automatics (none unless recursed)
|
||||||
|
for (const auto& it : oldValues) {
|
||||||
|
if (it.second) newValue(it.first, it.second);
|
||||||
|
}
|
||||||
|
if (returnp) newValue(nodep, returnp);
|
||||||
|
if (funcp->recursive()) {
|
||||||
|
UASSERT_OBJ(m_recurseCount > 0, nodep, "recurse underflow");
|
||||||
|
--m_recurseCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
%Error: t/t_func_recurse_param.v:15:26: Expecting expression to be constant, but can't determine constant for FUNCREF 'recurse_self'
|
|
||||||
: ... note: In instance 't'
|
|
||||||
t/t_func_recurse_param.v:9:27: ... Location of non-constant FUNC 'recurse_self': Unsupported: Recursive constant functions
|
|
||||||
15 | localparam int ZERO = recurse_self(0);
|
|
||||||
| ^~~~~~~~~~~~
|
|
||||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
|
||||||
%Error: t/t_func_recurse_param.v:16:28: Expecting expression to be constant, but can't determine constant for FUNCREF 'recurse_self'
|
|
||||||
: ... note: In instance 't'
|
|
||||||
t/t_func_recurse_param.v:9:27: ... Location of non-constant FUNC 'recurse_self': Unsupported: Recursive constant functions
|
|
||||||
16 | localparam int ELEVEN = recurse_self(3);
|
|
||||||
| ^~~~~~~~~~~~
|
|
||||||
%Error: Exiting due to
|
|
||||||
|
|
@ -11,9 +11,8 @@ import vltest_bootstrap
|
||||||
|
|
||||||
test.scenarios('simulator')
|
test.scenarios('simulator')
|
||||||
|
|
||||||
test.compile(fails=test.vlt_all, expect_filename=test.golden_filename)
|
test.compile()
|
||||||
|
|
||||||
if not test.vlt_all:
|
test.execute()
|
||||||
test.execute()
|
|
||||||
|
|
||||||
test.passes()
|
test.passes()
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,41 @@
|
||||||
// any use, without warranty, 2003 by Wilson Snyder.
|
// any use, without warranty, 2003 by Wilson Snyder.
|
||||||
// SPDX-License-Identifier: CC0-1.0
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
// verilog_format: off
|
||||||
|
`define stop $stop
|
||||||
|
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||||
|
// verilog_format: on
|
||||||
|
|
||||||
module t;
|
module t;
|
||||||
|
|
||||||
function automatic int recurse_self;
|
function automatic int recurse_self;
|
||||||
input int i;
|
input int i;
|
||||||
if (i == 0) recurse_self = 0;
|
int r1;
|
||||||
else recurse_self = i + recurse_self(i - 1) * 2;
|
int r2;
|
||||||
endfunction
|
// Simulator support for statics in constant functions get varying results, not testing
|
||||||
|
static int local_static = 10;
|
||||||
|
automatic int local_automatic; // check each function call resets to zero
|
||||||
|
if (i == 0) begin
|
||||||
|
local_static = 0;
|
||||||
|
recurse_self = 0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
local_static = local_static + 1;
|
||||||
|
local_automatic = local_automatic + 10;
|
||||||
|
recurse_self = i + recurse_self(i - 1) * 2 + recurse_self(i - 1) * 3 + local_automatic;
|
||||||
|
end
|
||||||
|
endfunction
|
||||||
|
|
||||||
localparam int ZERO = recurse_self(0);
|
localparam int F0 = recurse_self(0);
|
||||||
localparam int ELEVEN = recurse_self(3);
|
localparam int F3 = recurse_self(3);
|
||||||
|
localparam int F4 = recurse_self(4);
|
||||||
|
|
||||||
initial begin
|
initial begin
|
||||||
if (ZERO != 0) $stop;
|
`checkd(F0, 0);
|
||||||
if (ELEVEN != 11) $stop;
|
`checkd(F3, 348);
|
||||||
$write("*-* All Finished *-*\n");
|
`checkd(F4, 1754);
|
||||||
$finish;
|
$write("*-* All Finished *-*\n");
|
||||||
end
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,210 @@
|
||||||
%Error: t/t_func_recurse_param_bad.v:15:26: Expecting expression to be constant, but can't determine constant for FUNCREF 'recurse_self'
|
%Error: t/t_func_recurse_param_bad.v:15:25: Expecting expression to be constant, but can't determine constant for FUNCREF 'recurse_self'
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
t/t_func_recurse_param_bad.v:9:27: ... Location of non-constant FUNC 'recurse_self': Unsupported: Recursive constant functions
|
t/t_func_recurse_param_bad.v:9:26: ... Location of non-constant FUNC 'recurse_self': Constant function recursed more than 1000 times
|
||||||
15 | localparam int HUGE = recurse_self(10000);
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
| ^~~~~~~~~~~~
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
t/t_func_recurse_param_bad.v:12:29: ... Called from 'recurse_self()' with parameters:
|
||||||
|
i = 32'h2329
|
||||||
|
... stack truncated
|
||||||
|
15 | localparam int HUGE = recurse_self(10000);
|
||||||
|
| ^~~~~~~~~~~~
|
||||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,18 @@
|
||||||
|
|
||||||
module t;
|
module t;
|
||||||
|
|
||||||
function automatic int recurse_self;
|
function automatic int recurse_self;
|
||||||
input int i;
|
input int i;
|
||||||
if (i == 0) recurse_self = 0;
|
if (i == 0) recurse_self = 0;
|
||||||
else recurse_self = i + recurse_self(i - 1) * 2;
|
else recurse_self = i + recurse_self(i - 1) * 2;
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
localparam int HUGE = recurse_self(10000); // too much recursion
|
localparam int HUGE = recurse_self(10000); // too much recursion
|
||||||
|
|
||||||
initial begin
|
initial begin
|
||||||
$display(HUGE);
|
$display(HUGE);
|
||||||
$write("*-* All Finished *-*\n");
|
$write("*-* All Finished *-*\n");
|
||||||
$finish;
|
$finish;
|
||||||
end
|
end
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue