Merge pull request #677 from larsclausen/recursive-return

Handle recursive functions using `return`
This commit is contained in:
Stephen Williams 2022-04-11 19:58:59 -07:00 committed by GitHub
commit 53fd42f3da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 172 additions and 10 deletions

View File

@ -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 );

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,3 @@
factorial 3 = 6
factorial 4 = 24
factorial 5 = 120

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
{
}

View File

@ -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&);

View File

@ -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);

View File

@ -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);

View File

@ -843,6 +843,7 @@ struct ivl_statement_s {
struct { /* IVL_ST_DISABLE */
ivl_scope_t scope;
bool flow_control;
} disable_;
struct { /* IVL_ST_FOREVER */

View File

@ -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.");

View File

@ -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);

View File

@ -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} },

View File

@ -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

View File

@ -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.