Merge pull request #677 from larsclausen/recursive-return
Handle recursive functions using `return`
This commit is contained in:
commit
53fd42f3da
|
|
@ -5691,7 +5691,7 @@ NetProc* PReturn::elaborate(Design*des, NetScope*scope) const
|
|||
des->errors += 1;
|
||||
return 0;
|
||||
}
|
||||
NetDisable*disa = new NetDisable(target);
|
||||
NetDisable*disa = new NetDisable(target, true);
|
||||
disa->set_line( *this );
|
||||
return disa;
|
||||
}
|
||||
|
|
@ -5720,7 +5720,7 @@ NetProc* PReturn::elaborate(Design*des, NetScope*scope) const
|
|||
assn->set_line( *this );
|
||||
proc->append(assn);
|
||||
|
||||
NetDisable*disa = new NetDisable(target);
|
||||
NetDisable*disa = new NetDisable(target, true);
|
||||
disa->set_line( *this );
|
||||
proc->append( disa );
|
||||
|
||||
|
|
|
|||
3
ivl.def
3
ivl.def
|
|
@ -292,8 +292,9 @@ ivl_stmt_delay_expr
|
|||
ivl_stmt_delay_val
|
||||
ivl_stmt_events
|
||||
ivl_stmt_file
|
||||
ivl_stmt_lineno
|
||||
ivl_stmt_flow_control
|
||||
ivl_stmt_lexp
|
||||
ivl_stmt_lineno
|
||||
ivl_stmt_lval
|
||||
ivl_stmt_lvals
|
||||
ivl_stmt_lwidth
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
# include <inttypes.h>
|
||||
# include <stddef.h>
|
||||
# include <stdbool.h>
|
||||
|
||||
/* Re the _CLASS define: clang++ wants this to be class to match the
|
||||
* definition, but clang (the C) compiler needs it to be a struct
|
||||
|
|
@ -2259,6 +2260,8 @@ extern uint64_t ivl_stmt_delay_val(ivl_statement_t net);
|
|||
extern unsigned ivl_stmt_needs_t0_trigger(ivl_statement_t net);
|
||||
extern unsigned ivl_stmt_nevent(ivl_statement_t net);
|
||||
extern ivl_event_t ivl_stmt_events(ivl_statement_t net, unsigned idx);
|
||||
/* IVL_ST_DISABLE */
|
||||
extern bool ivl_stmt_flow_control(ivl_statement_t net);
|
||||
/* IVL_ST_CONTRIB */
|
||||
extern ivl_expr_t ivl_stmt_lexp(ivl_statement_t net);
|
||||
/* IVL_ST_ASSIGN IVL_ST_ASSIGN_NB IVL_ST_CASSIGN IVL_ST_DEASSIGN
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
factorial 3 = 6
|
||||
factorial 4 = 24
|
||||
factorial 5 = 120
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// Check that recursive functions are supported when the `return` statement is
|
||||
// used.
|
||||
|
||||
module recursive_func();
|
||||
|
||||
function automatic [15:0] factorial(input [15:0] n);
|
||||
if (n > 1) begin : recursion
|
||||
reg [15:0] result;
|
||||
result = factorial(n - 1) * n;
|
||||
return result;
|
||||
end
|
||||
|
||||
return n;
|
||||
endfunction
|
||||
|
||||
reg [15:0] r1;
|
||||
reg [15:0] r2;
|
||||
reg [15:0] r3;
|
||||
|
||||
initial begin
|
||||
fork
|
||||
r1 = factorial(3);
|
||||
r2 = factorial(4);
|
||||
r3 = factorial(5);
|
||||
join
|
||||
$display("factorial 3 = %0d", r1);
|
||||
$display("factorial 4 = %0d", r2);
|
||||
$display("factorial 5 = %0d", r3);
|
||||
end
|
||||
|
||||
wire [15:0] r4;
|
||||
wire [15:0] r5;
|
||||
wire [15:0] r6;
|
||||
|
||||
assign r4 = factorial(6);
|
||||
assign r5 = factorial(7);
|
||||
assign r6 = factorial(8);
|
||||
|
||||
initial begin
|
||||
#1;
|
||||
$display("factorial 6 = %0d", r4);
|
||||
$display("factorial 7 = %0d", r5);
|
||||
$display("factorial 8 = %0d", r6);
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Check that constant recursive functions are supported.
|
||||
|
||||
module recursive_func();
|
||||
|
||||
function automatic [15:0] factorial;
|
||||
|
||||
input [15:0] n;
|
||||
|
||||
begin
|
||||
factorial = (n > 1) ? factorial(n - 1) * n : n;
|
||||
end
|
||||
|
||||
endfunction
|
||||
|
||||
localparam F3 = factorial(3);
|
||||
localparam F4 = factorial(4);
|
||||
localparam F5 = factorial(5);
|
||||
|
||||
initial begin
|
||||
$display("factorial 3 = %0d", F3);
|
||||
$display("factorial 4 = %0d", F4);
|
||||
$display("factorial 5 = %0d", F5);
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// Check that constant recursive functions are supported when the `return`
|
||||
// statement is used.
|
||||
|
||||
module recursive_func();
|
||||
|
||||
function automatic [15:0] factorial(input [15:0] n);
|
||||
if (n > 1) begin
|
||||
return factorial(n - 1) * n;
|
||||
end
|
||||
return n;
|
||||
endfunction
|
||||
|
||||
localparam F3 = factorial(3);
|
||||
localparam F4 = factorial(4);
|
||||
localparam F5 = factorial(5);
|
||||
|
||||
initial begin
|
||||
$display("factorial 3 = %0d", F3);
|
||||
$display("factorial 4 = %0d", F4);
|
||||
$display("factorial 5 = %0d", F5);
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -402,6 +402,8 @@ program5a CE,-g2009 ivltests
|
|||
program5b CE,-g2009 ivltests
|
||||
program_hello normal,-g2009 ivltests
|
||||
program_hello2 CE,-g2009 ivltests
|
||||
recursive_func2 normal,-g2005-sv ivltests gold=recursive_func.gold
|
||||
recursive_func_const2 normal,-g2005-sv ivltests gold=recursive_func_const.gold
|
||||
sbyte_test normal,-g2005-sv ivltests
|
||||
scalar_vector normal,-g2005-sv ivltests
|
||||
sf_countbits normal,-g2012 ivltests
|
||||
|
|
|
|||
|
|
@ -1469,7 +1469,8 @@ real_force_rel normal ivltests
|
|||
real_invalid_ops CE ivltests gold=real_invalid_ops.gold
|
||||
real_logical normal ivltests
|
||||
real_reg_force_rel normal ivltests
|
||||
recursive_func normal ivltests gold=recursive_func.gold
|
||||
recursive_func1 normal ivltests gold=recursive_func.gold
|
||||
recursive_func_const1 normal ivltests gold=recursive_func_const.gold
|
||||
recursive_task normal ivltests gold=recursive_task.gold
|
||||
redef_net_error CE ivltests
|
||||
redef_reg_error CE ivltests
|
||||
|
|
|
|||
|
|
@ -90,7 +90,10 @@ pr2172606b CE ivltests
|
|||
pr2276163 CE ivltests
|
||||
pr2929913 CE ivltests
|
||||
real_events CE ivltests
|
||||
recursive_func CE ivltests
|
||||
recursive_func1 CE ivltests
|
||||
recursive_func2 CE ivltests
|
||||
recursive_func_const1 CE ivltests
|
||||
recursive_func_const2 CE ivltests
|
||||
recursive_task CE ivltests
|
||||
task_init_var1 CE,-pallowsigned=1 ivltests
|
||||
task_init_var2 CE,-pallowsigned=1 ivltests
|
||||
|
|
|
|||
|
|
@ -174,8 +174,8 @@ void NetCase::prune()
|
|||
}
|
||||
}
|
||||
|
||||
NetDisable::NetDisable(NetScope*tgt)
|
||||
: target_(tgt)
|
||||
NetDisable::NetDisable(NetScope*tgt, bool flow_control)
|
||||
: target_(tgt), flow_control_(flow_control)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3272,10 +3272,11 @@ class NetDeassign : public NetAssignBase {
|
|||
class NetDisable : public NetProc {
|
||||
|
||||
public:
|
||||
explicit NetDisable(NetScope*tgt);
|
||||
explicit NetDisable(NetScope*tgt, bool flow_control = false);
|
||||
~NetDisable();
|
||||
|
||||
const NetScope*target() const;
|
||||
bool flow_control() const { return flow_control_; }
|
||||
|
||||
virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false,
|
||||
bool nested_func = false) const;
|
||||
|
|
@ -3288,6 +3289,11 @@ class NetDisable : public NetProc {
|
|||
|
||||
private:
|
||||
NetScope*target_;
|
||||
// If false all threads in the target_ scope are disabled. If true only
|
||||
// the closest thread in thread hierachy of the target_ scope is
|
||||
// disabled. The latter is used to implement flow control statements like
|
||||
// `return`.
|
||||
bool flow_control_;
|
||||
|
||||
private: // not implemented
|
||||
NetDisable(const NetDisable&);
|
||||
|
|
|
|||
|
|
@ -2713,6 +2713,11 @@ extern "C" ivl_scope_t ivl_stmt_call(ivl_statement_t net)
|
|||
}
|
||||
}
|
||||
|
||||
extern "C" bool ivl_stmt_flow_control(ivl_statement_t net)
|
||||
{
|
||||
return net->u_.disable_.flow_control;
|
||||
}
|
||||
|
||||
extern "C" unsigned ivl_stmt_case_count(ivl_statement_t net)
|
||||
{
|
||||
assert(net);
|
||||
|
|
|
|||
|
|
@ -657,6 +657,7 @@ bool dll_target::proc_disable(const NetDisable*net)
|
|||
FILE_NAME(stmt_cur_, net);
|
||||
|
||||
stmt_cur_->type_ = IVL_ST_DISABLE;
|
||||
stmt_cur_->u_.disable_.flow_control = net->flow_control();
|
||||
const NetScope* dis_scope = net->target();
|
||||
/* A normal disable. */
|
||||
if (dis_scope) stmt_cur_->u_.disable_.scope = lookup_scope_(dis_scope);
|
||||
|
|
|
|||
1
t-dll.h
1
t-dll.h
|
|
@ -843,6 +843,7 @@ struct ivl_statement_s {
|
|||
|
||||
struct { /* IVL_ST_DISABLE */
|
||||
ivl_scope_t scope;
|
||||
bool flow_control;
|
||||
} disable_;
|
||||
|
||||
struct { /* IVL_ST_FOREVER */
|
||||
|
|
|
|||
|
|
@ -1340,8 +1340,13 @@ static int show_stmt_disable(ivl_statement_t net, ivl_scope_t sscope)
|
|||
|
||||
/* A normal disable statement. */
|
||||
if (target) {
|
||||
show_stmt_file_line(net, "Disable statement.");
|
||||
fprintf(vvp_out, " %%disable S_%p;\n", target);
|
||||
if (ivl_stmt_flow_control(net)) {
|
||||
show_stmt_file_line(net, "Flow control disable statement.");
|
||||
fprintf(vvp_out, " %%disable/flow S_%p;\n", target);
|
||||
} else {
|
||||
show_stmt_file_line(net, "Disable statement.");
|
||||
fprintf(vvp_out, " %%disable S_%p;\n", target);
|
||||
}
|
||||
/* A SystemVerilog disable fork statement. */
|
||||
} else {
|
||||
show_stmt_file_line(net, "Disable fork statement.");
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ extern bool of_DELETE_ELEM(vthread_t thr, vvp_code_t code);
|
|||
extern bool of_DELETE_OBJ(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_DELETE_TAIL(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_DISABLE(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_DISABLE_FLOW(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_DISABLE_FORK(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_DIV(vthread_t thr, vvp_code_t code);
|
||||
extern bool of_DIV_S(vthread_t thr, vvp_code_t code);
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ static const struct opcode_table_s opcode_table[] = {
|
|||
{ "%delete/obj",of_DELETE_OBJ,1,{OA_FUNC_PTR,OA_NONE, OA_NONE} },
|
||||
{ "%delete/tail",of_DELETE_TAIL,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} },
|
||||
{ "%disable", of_DISABLE, 1, {OA_VPI_PTR,OA_NONE, OA_NONE} },
|
||||
{ "%disable/flow", of_DISABLE_FLOW, 1, {OA_VPI_PTR,OA_NONE, OA_NONE} },
|
||||
{ "%disable/fork",of_DISABLE_FORK,0,{OA_NONE,OA_NONE, OA_NONE} },
|
||||
{ "%div", of_DIV, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
{ "%div/s", of_DIV_S, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||
|
|
|
|||
|
|
@ -469,6 +469,17 @@ This instruction terminates threads that are part of a specific
|
|||
scope. The label identifies the scope in question, and the threads are
|
||||
the threads that are currently within that scope.
|
||||
|
||||
* %disable/flow <scope-label>
|
||||
|
||||
This instruction is similar to `%disable` except that it will only disable a
|
||||
single thread of the specified scope. The disabled thread will be the thread
|
||||
closest to the current thread in the thread hierarchy. This can either be thread
|
||||
itself or one of its parents.
|
||||
|
||||
It is used to implement flow control statements called from within a thread that
|
||||
only affect the thread or its parents. E.g. SystemVerilog `return`, `continue`
|
||||
or `break`.
|
||||
|
||||
* %disable/fork
|
||||
|
||||
This instruction terminates all the detached children for the current
|
||||
|
|
|
|||
|
|
@ -2718,6 +2718,30 @@ bool of_DISABLE(vthread_t thr, vvp_code_t cp)
|
|||
return ! disabled_myself_flag;
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar to `of_DISABLE`. But will only disable a single thread of the
|
||||
* specified scope. The disabled thread will be the thread closest to the
|
||||
* current thread in thread hierarchy. This can either be the current thread,
|
||||
* either the thread itself or one of its parents.
|
||||
* This is used for SystemVerilog flow control instructions like `return`,
|
||||
* `continue` and `break`.
|
||||
*/
|
||||
|
||||
bool of_DISABLE_FLOW(vthread_t thr, vvp_code_t cp)
|
||||
{
|
||||
__vpiScope*scope = static_cast<__vpiScope*>(cp->handle);
|
||||
vthread_t cur = thr;
|
||||
|
||||
while (cur && cur->parent_scope != scope)
|
||||
cur = cur->parent;
|
||||
|
||||
assert(cur);
|
||||
if (cur)
|
||||
return !do_disable(cur, thr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implement the %disable/fork (SystemVerilog) instruction by disabling
|
||||
* all the detached children of the given thread.
|
||||
|
|
|
|||
Loading…
Reference in New Issue