Recursive tasks and disable

P

Paul Jansen

Guest
Problem #1: I have a task which I need to return from/disable early.
This means that I can't use inout or output parameters to return data
from the task.

Problem #2: The task needs to be recursive.

I can't imemdiately see how to implement this. I can get around #1 by
writing return values to global variables, but this then means that
the task can't be recursive, because all instances of it will attempt
to write their results to the same variables. Without pointers, I
can't see how to make an assignment to a global actually assign to
different variables for different instances of the task.

Any ideas on how to do this?

Actually, now that I've got around to writing this down, it seems that
I could do this with a block disable, rather than a task disable:

task x;
input a;
output b;
begin : innards
...
if(need_to_return)
disable innards;
...
x(fu, bar);
...
end // innards
// disable to here - output b written correctly?
endtask

Comments/other ideas? And presumably I can't do anything recursive at
all in Verilog-1995?

Thanks -

/PJ
 
On Thu, 11 Jan 2007 18:16:16 +0000, Paul Jansen
<pj30145@yahoo.com> wrote:

Problem #1: I have a task which I need to return from/disable early.
This means that I can't use inout or output parameters to return data
from the task.

Problem #2: The task needs to be recursive.
[...]

Actually, now that I've got around to writing this down, it seems that
I could do this with a block disable, rather than a task disable:

task x;
input a;
output b;
begin : innards
...
if(need_to_return)
disable innards;
...
x(fu, bar);
...
end // innards
// disable to here - output b written correctly?
endtask

Comments/other ideas?
What you've said looks fine to me. I guess I might be tempted to
enclose the "innards" in yet another begin...end, to protect myself
against future code changes somewhat and also to make it possible
to do unconditional cleanups just before returning.
Self-disable from the inside of a begin..end block is a shade
ugly syntactically, but it's not too hard to reason about.

If you have a SystemVerilog simulator, you can do a return
statement (a la C) in any task or function.

And presumably I can't do anything recursive at
all in Verilog-1995?
Urrrm, there's always the usual stuff about converting recursive
code to iterative, and you can do tail-recursion easily enough...
and if you were truly desperate you could use integer indexing
into an array to mimic what you would normally do with
pointers and/or a stack. But no, for all practical purposes
you need "task automatic" from V-2001.
--
Jonathan Bromley, Consultant

DOULOS - Developing Design Know-how
VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services

Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK
jonathan.bromley@MYCOMPANY.com
http://www.MYCOMPANY.com

The contents of this message may contain personal views which
are not the views of Doulos Ltd., unless specifically stated.
 
Paul Jansen wrote:
Problem #1: I have a task which I need to return from/disable early.
This means that I can't use inout or output parameters to return data
from the task.
The LRM states that the result of output and inout arguments is
unspecified if the task is disabled. You can work around this for a
disable of the task itself by putting a named block immediately inside
the task body and disabling that. However, that won't help for a task
call being disabled indirectly by disabling another task or block from
which it was called.

Problem #2: The task needs to be recursive.

Any ideas on how to do this?

Actually, now that I've got around to writing this down, it seems that
I could do this with a block disable, rather than a task disable:
But note that a disable will disable ALL activations of a block or
task.

So if you are 3 levels deep in nested recursive calls, a disable of the
block will disable all the activations, leaving you at the end of the
outermost entry to the block. This is not the same as a return
statement. It does not just terminate the innermost call.

And if 2 different initial blocks have called the same instance of a
task, and one of them disables the task (or block in the task), then
both processes/threads will exit the task or block. It does not just
disable the process that performed the disable.

After a disable of a task or block instance, there will be nothing
executing inside that scope. A disable is a sledgehammer, and is not
very good for fine control.

Comments/other ideas?
Look at how constructs like "break" and "continue" from C are coded in
structured languages that don't have those constructs (or how "return"
is coded in a language that doesn't have that, but I can't think of any
common ones off-hand).

For example, you enclose the rest of the body of the task in an
if-statement, so that you only execute it if you aren't trying to
return. If you are trying to return from inside something more
complex, like a loop, add a boolean flag that indicates you are getting
out. Then use an if-statement to skip the rest of the loop, add the
boolean flag to the exit conditions for the loop, and add another
if-statement to skip everything after the loop to the end of the task.
It is clumsy, but it works. A goto (and a return is just a limited
goto) can always be replaced by structured constructs with some effort.

And presumably I can't do anything recursive at
all in Verilog-1995?
Not without a lot of impractical effort.
 
Hi Steve -

I've only got a couple of minutes to check through your post before
going to bed (!) but, if I understand you correctly, you're saying
that a block disable executed within an automatic task will disable
the block in *all* instances of the task. Is that correct? Doesn't
seem like very 'automatic' behaviour. In this app, the task is first
enabled from a single initial block, and then re-enables itself
indefinitely, until the 'leaf' instance disables itself to initiate a
chain of returns. In other words, normal recursive behaviour in other
languages.

However, your suggestion of restructuring to remove the 'return' looks
like it will work. I know I can implement 'goto' doing this, so
clearly I can implement a 'return'. It's hard work, though, so I'd
only do it as a last resort.

Of course, my next problem is finding simulators that support
automatic tasks. I've tried two, and one of them (Icarus) doesn't. I
guess that all the commercial ones do now.

Thanks -

/PJ
 
On Thu, 11 Jan 2007 19:50:14 +0000, Jonathan Bromley

If you have a SystemVerilog simulator, you can do a return
statement (a la C) in any task or function.
I don't... I'm not going to get one, either :)

On the other hand, SystemVerilog++ will be here before long. I might
wait for that one.
 
Paul Jansen wrote:

if I understand you correctly, you're saying
that a block disable executed within an automatic task will disable
the block in *all* instances of the task. Is that correct?
All activations or invocations of the same task instance, not all
instances of the task. A different task instance would be the task in
a different instance of the same module, which is quite different, and
would not be affected by disabling this task instance.

Doesn't
seem like very 'automatic' behaviour.
That shouldn't be too surprising, since disables were part of the
language long before automatic tasks were added. Disables also have
other nonlocal capabilities, such as the ability to disable a block
from another process that is not executing in the block. Since there
is not required to be any connection between the process doing the
disable and the process(es) being disabled, there is no way to
distinguish which of multiple processes to disable. So the language
has always specified to disable them all.

Disables have to handle other situations where they need to disable
multiple processes executing in a block, even without task re-entrance.
For example, a process inside the block could have called another task
which executes a fork that creates multiple subprocesses and then waits
for them to finish so that it can continue after the join. Disabling
that block requires also disabling all those forked subprocesses that
the process executing in the block is waiting on. This must happen
even though the fork statement may not be physically inside the
disabled block. Disables in Verilog are much more complex than a
return, break or continue statement.

More on 'automatic' behavior:
When automatic tasks were grafted onto the language, they didn't fit
well with a number of other language features that existed before them.
There are restrictions on other Verilog language constructs when
combined with automatic variables, because they were never designed to
work in a language with automatic variables. That is the problem with
grafting new features onto a language.

In this app, the task is first
enabled from a single initial block, and then re-enables itself
indefinitely, until the 'leaf' instance disables itself to initiate a
chain of returns. In other words, normal recursive behaviour in other
languages.
A disable will disable the entire chain at once. It is not a return.

From your description of initiating a chain of returns, it sounds like
your tasks don't do anything after the recursive call returns except to
return themselves. This is something called "tail recursion". One
property of "tail recursion" is that the recursion can always be
removed to create a non-recursive definition, which loops back to the
top of the routine instead of doing a recursive call. Since you were
asking about running this in a simulator that did not support automatic
tasks, you might want to look into how to remove tail recursion.
Like a return, it could be awkward to implement without a goto though.

Of course, my next problem is finding simulators that support
automatic tasks. I've tried two, and one of them (Icarus) doesn't. I
guess that all the commercial ones do now.
All the major ones certainly.
 

Welcome to EDABoard.com

Sponsor

Back
Top