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 <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2022-02-06 15:18:30 +01:00
parent b210eb8264
commit 730c9c28b2
2 changed files with 37 additions and 8 deletions

View File

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

View File

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