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:
parent
b210eb8264
commit
730c9c28b2
39
elaborate.cc
39
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()) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue