From 730c9c28b22a9281c03d2fa9f689de845883ef5e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 6 Feb 2022 15:18:30 +0100 Subject: [PATCH] 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;