From 730c9c28b22a9281c03d2fa9f689de845883ef5e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 6 Feb 2022 15:18:30 +0100 Subject: [PATCH 1/2] Support return in tasks SystemVerilog allows to use the `return` statement in a task to exit the task before it reaches the end of its execution. This is defined in section 13.3 ("Tasks") of the LRM (1800-2017). This is similar to using `disable` to stop a task from within itself with the difference that `disable` will affect all concurrently running executions of a task, while `return` will only affect the task from which it has been called. The `%disable/flow` vvp instruction allows to implement the required behavior for task return. There is one complication in that it is not allowed to call return from inside a parallel block (fork-join). If a parallel block is unnamed and has no variable declarations there won't be a NetScope for it. So it is not possible to detect whether the return is inside a parallel block by walking up the scope chain. To solve this add a design global counter that gets incremented when entering a fork block and decremented when exiting a parallel block. The return implementation then checks if the counter is non 0 to determine whether it is in a parallel block. Signed-off-by: Lars-Peter Clausen --- elaborate.cc | 39 +++++++++++++++++++++++++++++++-------- netlist.h | 6 ++++++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/elaborate.cc b/elaborate.cc index 27d2ea350..1437a3950 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -2977,6 +2977,9 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const return tmp; } + if (type != NetBlock::SEQU) + des->fork_enter(); + for (unsigned idx = 0 ; idx < list_.size() ; idx += 1) { assert(list_[idx]); @@ -3018,6 +3021,9 @@ NetProc* PBlock::elaborate(Design*des, NetScope*scope) const cur->append(tmp); } + if (type != NetBlock::SEQU) + des->fork_exit(); + // Update flags in parent scope. if (!nscope->is_const_func()) scope->is_const_func(false); @@ -5763,23 +5769,27 @@ NetProc* PRepeat::elaborate(Design*des, NetScope*scope) const NetProc* PReturn::elaborate(Design*des, NetScope*scope) const { NetScope*target = scope; + + if (des->is_in_fork()) { + cerr << get_fileline() << ": error: " + << "Return statement is not allowed within fork-join block." << endl; + des->errors += 1; + return 0; + } + for (;;) { if (target == 0) { cerr << get_fileline() << ": error: " - << "Return statement is not in a function." << endl; + << "Return statement is not in a function or task." + << endl; des->errors += 1; return 0; } if (target->type() == NetScope::FUNC) break; - - if (target->type() == NetScope::TASK) { - cerr << get_fileline() << ": error: " - << "Cannot \"return\" from tasks." << endl; - des->errors += 1; - return 0; - } + if (target->type() == NetScope::TASK) + break; if (target->type()==NetScope::BEGIN_END) { target = target->parent(); @@ -5791,6 +5801,19 @@ NetProc* PReturn::elaborate(Design*des, NetScope*scope) const des->errors += 1; return 0; } + + if (target->type() == NetScope::TASK) { + if (expr_) { + cerr << get_fileline() << ": error: " + << "A value cannot be returned from a task." << endl; + des->errors += 1; + return 0; + } + NetDisable *disa = new NetDisable(target, true); + disa->set_line(*this); + return disa; + } + ivl_assert(*this, target->type() == NetScope::FUNC); if (target->func_def()->is_void()) { diff --git a/netlist.h b/netlist.h index 56ae221ff..7b9eb05d3 100644 --- a/netlist.h +++ b/netlist.h @@ -5124,6 +5124,12 @@ class Design { // detected. It prevents code being emitted. unsigned errors; + void fork_enter() { in_fork++; }; + void fork_exit() { in_fork--; }; + bool is_in_fork() { return in_fork != 0; } + + unsigned int in_fork = 0; + private: NetScope* find_scope_(NetScope*, const hname_t&name, NetScope::TYPE type = NetScope::MODULE) const; From d1aecf452ca9737f29bce7c96f35a601321ad779 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 6 Feb 2022 15:27:24 +0100 Subject: [PATCH 2/2] Add regression test for return in tasks Check support for using the return statement in a task. * That it is possible to exit form a task using the `return` statement without affecting other concurrently running instances of the same task * That it is possible to use return in a named block in a task * That using a return value in a task results in a elaboration error * Returning from inside a parallel block in a task results in a elaboration error Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/task_return1.v | 31 +++++++++++++++++++++++++ ivtest/ivltests/task_return2.v | 22 ++++++++++++++++++ ivtest/ivltests/task_return_fail1.v | 15 ++++++++++++ ivtest/ivltests/task_return_fail2.v | 18 ++++++++++++++ ivtest/regress-vvp.list | 4 ++++ ivtest/vvp_tests/task_return1.json | 5 ++++ ivtest/vvp_tests/task_return2.json | 5 ++++ ivtest/vvp_tests/task_return_fail1.json | 5 ++++ ivtest/vvp_tests/task_return_fail2.json | 5 ++++ 9 files changed, 110 insertions(+) create mode 100644 ivtest/ivltests/task_return1.v create mode 100644 ivtest/ivltests/task_return2.v create mode 100644 ivtest/ivltests/task_return_fail1.v create mode 100644 ivtest/ivltests/task_return_fail2.v create mode 100644 ivtest/vvp_tests/task_return1.json create mode 100644 ivtest/vvp_tests/task_return2.json create mode 100644 ivtest/vvp_tests/task_return_fail1.json create mode 100644 ivtest/vvp_tests/task_return_fail2.json diff --git a/ivtest/ivltests/task_return1.v b/ivtest/ivltests/task_return1.v new file mode 100644 index 000000000..ae96bffba --- /dev/null +++ b/ivtest/ivltests/task_return1.v @@ -0,0 +1,31 @@ +// Check that it is possible to exit from a task using the return statement with +// affecting other concurrently running instances of the same task. + +module test; + + task automatic t(input integer a, output integer b); + if (a == 0) begin + b = 1; + return; + end + #10 + b = 100; + endtask + + integer b1; + integer b2; + + initial begin + fork + t(0, b1); + t(1, b2); + join + + if (b1 == 1 && b2 == 100) begin + $display("PASSED"); + end else begin + $display("FAILED b1=%0d, b2=%0d", b1, b2); + end + end + +endmodule diff --git a/ivtest/ivltests/task_return2.v b/ivtest/ivltests/task_return2.v new file mode 100644 index 000000000..d82c8fc64 --- /dev/null +++ b/ivtest/ivltests/task_return2.v @@ -0,0 +1,22 @@ +// Check that it is possible to return from a named sub-block of a task using a +// `return` statement. + +module test; + + task t(input integer a); + begin : subblock + if (a == 1) begin : condition + return; + end + end + $display("FAILED"); + $finish; + endtask + + initial begin + t(1); + #10 + $display("PASSED"); + end + +endmodule diff --git a/ivtest/ivltests/task_return_fail1.v b/ivtest/ivltests/task_return_fail1.v new file mode 100644 index 000000000..044116e16 --- /dev/null +++ b/ivtest/ivltests/task_return_fail1.v @@ -0,0 +1,15 @@ +// Check that using a return value when using the return statement in a task +// results in an error. + +module test; + + task t; + return 10; // This is an error, tasks can not have return values. + endtask + + initial begin + t(); + $display("FAILED"); + end + +endmodule diff --git a/ivtest/ivltests/task_return_fail2.v b/ivtest/ivltests/task_return_fail2.v new file mode 100644 index 000000000..a6b6ac37e --- /dev/null +++ b/ivtest/ivltests/task_return_fail2.v @@ -0,0 +1,18 @@ +// Check that using a return statment inside a parallel block in a task results +// in an error. + +module test; + + task t; + fork + // This is an error it is not possible to return from inside a parallel block + return; + join + endtask + + initial begin + t(); + $display("FAILED"); + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 2dd09bb7a..01a976b3c 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -36,3 +36,7 @@ struct_packed_write_read2 vvp_tests/struct_packed_write_read2.json sv_foreach9 vvp_tests/sv_foreach9.json sv_foreach10 vvp_tests/sv_foreach10.json sdf_header vvp_tests/sdf_header.json +task_return1 vvp_tests/task_return1.json +task_return2 vvp_tests/task_return2.json +task_return_fail1 vvp_tests/task_return_fail1.json +task_return_fail2 vvp_tests/task_return_fail2.json diff --git a/ivtest/vvp_tests/task_return1.json b/ivtest/vvp_tests/task_return1.json new file mode 100644 index 000000000..d6fcc1d2c --- /dev/null +++ b/ivtest/vvp_tests/task_return1.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "task_return1.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/task_return2.json b/ivtest/vvp_tests/task_return2.json new file mode 100644 index 000000000..1bac64c1d --- /dev/null +++ b/ivtest/vvp_tests/task_return2.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "task_return2.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/task_return_fail1.json b/ivtest/vvp_tests/task_return_fail1.json new file mode 100644 index 000000000..3511d078c --- /dev/null +++ b/ivtest/vvp_tests/task_return_fail1.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "task_return_fail1.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/task_return_fail2.json b/ivtest/vvp_tests/task_return_fail2.json new file mode 100644 index 000000000..0ea73f5b4 --- /dev/null +++ b/ivtest/vvp_tests/task_return_fail2.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "task_return_fail2.v", + "iverilog-args" : [ "-g2005-sv" ] +}