RE: [sv-ec] RE: [sv-bc] Can a function contain a fork/join/any/none?

From: Rich, Dave <Dave_Rich_at_.....>
Date: Sun Feb 26 2006 - 08:30:51 PST
Here's a twist:

 

What if classes had both new function and new task methods and either or
both could be overridden. The task could be called after the function
completes. This way we would know that a class that overrides its 'new'
task method has the potential to create a thread and we could limit the
construction of classes with an overridden new task method to places we
decide is legal to start a thread. This also avoids the mucky issue of
having functions create threads.

 

 

Dave

 

 

________________________________

From: owner-sv-bc@eda.org [mailto:owner-sv-bc@eda.org] On Behalf Of
Arturo Salz
Sent: Wednesday, February 22, 2006 10:50 AM
To: Joao Geada
Cc: Sv-Bc; sv-ec@eda.org
Subject: RE: [sv-ec] RE: [sv-bc] Can a function contain a
fork/join/any/none?

 

Joao,

 

Verilog does distinguish between functions and tasks, but as I wrote
before, functions are not limited to model purely combinational
behavior. Verilog allows functions to write to variables as side
effects, and these side effect assignments can spawn additional
activity. In fact, this is the reason SystemVerilog introduced the
"pure" keyword to denote DPI functions that exhibit this property of
purely combinational behavior.

 

As for your question, I know of three reasons why constructors ought to
be functions:

 

1) The statement invoking the constructor:

object = new( x, y, z );

resembles a function call from a syntactic as well as a semantic
perspective. Semantically speaking, calling a constructor (the new
method) causes a new object to be created and initialized, and a handle
to the newly created object to be returned to the invoking context. This
is exactly how functions with side effects behave --- in this case the
side effect is the creation and initialization of the new object.

Hence, if it looks like a duck and it quacks like duck, shouldn't we
consider it to be a duck? :-)

 

2) If constructors are defined to be tasks, it follows that constructors
must allow arbitrary blocking statements. However, blocking constructors
are very problematic because they lead to partially constructed objects,
which violate basic object-oriented principles. As an example of the
problems that blocking constructors may create, consider this scenario:

a)   class C extends class B which extends class A

b)   B's constructor contains a blocking statement that may sometimes
suspend the calling thread and wait for some event to occur.

c)   class A includes a virtual method that is overridden by class C
(and perhaps by class B)

If the constructor of class A adds the handle to the newly created
object (i.e., this) to some a container, such as a list, and sometime
later the virtual method (that is overridden by class C) is invoked
through that container then the virtual method might sometimes happens
before object C has been properly initialized, that is, C::new might not
yet have been executed because B::new was suspended. By allowing
blocking constructors, the language adds the ability to refer to
un-initialized objects, thereby violating the basic OO premise the every
object is fully and properly initialized after calling its constructor.

This might seem like a farfetched scenario, but I have seen such
problems in Vera, in which blocking class constructors are allowed. The
above scenario is not the only problematic one, just an example of a
family of problems that are extremely hard to identify.

 

3) At some point the language should allow creation of a derived object
without having to create a temporary object of the derived class. For
that operation, a cast would be the most natural extension:

base_class b = derived'new();

However, if constructors are defined to be tasks then the above
extension will be difficult to accommodate.

 

In summary, there are very good reasons why we don't want constructors
to become generalized (blocking) tasks. And, it was precisely for these
reasons that constructors were defined as functions. This is one area
where compilers can help users avoid critical mistakes that can be very
hard to find. So, regardless of whether a function is allowed to create
background threads (via fork/join_none) or not, class constructors
should have function semantics and disallow blocking statements.

 

 

            Arturo

 

________________________________

From: Joao Geada [mailto:jgeada@comcast.net] 
Sent: Sunday, February 19, 2006 2:58 PM
To: Arturo Salz
Cc: Sv-Bc
Subject: Re: [sv-ec] RE: [sv-bc] Can a function contain a
fork/join/any/none?

 

Arturo,

 

any particular reason why in those cases the constructor could not be a
task?

 

Verilog has always maintained the distinction between functions
(intended to

model purely combinational behavior, almost like pure mathematical
functions)

and tasks (everything else). I don't see why this handy conceptual
shorthand

needs to be broken. Given the intended behavior 'task' is the best
conceptual

bucket ...

 

Joao

	----- Original Message ----- 

	From: Arturo Salz <mailto:Arturo.Salz@synopsys.com>  

	To: sv-ec@eda.org ; sv-bc@eda.org 

	Sent: Friday, February 17, 2006 7:52 PM

	Subject: RE: [sv-ec] RE: [sv-bc] Can a function contain a
fork/join/any/none?

	 

	As Dave has learned, forking off background threads in class
constructors

	is a very common usage in the world of transaction modeling. A
transactor

	will typically create one or more threads for each instance
created during the

	simulation. The primary motivation of model writers is to
encapsulate all the

	inner workings inside the model and not expose the modeling
details to users 

	of the model.

	 

	When 1364-2001 writes "A function shall execute in one
simulation time unit", I

	understand that users take that quite literally as the meaning
of "time consuming".

	And, after introducing fork/join_none, which is definitely not
time consuming, I

	can understand why many people would conclude that
fork/join_none must be 

	allowed in functions. Frankly, I see nothing wrong with allowing
fork/join_none in

	functions. A fork/join_none is semantically equivalent to
triggering the execution

	of one or more threads from within the function, as in the
following code:

	 

	            function void F();                       function
void F();

	                // some initialization                   // some
initialization

	                fork                                          ->
ev;

	                    T1();
endfunction

	                    T2();

	                join_none                              always
@ev T1();

	            endfunction                               always @ev
T2();

	 

	Both versions of the above function F() accomplish the same
thing, except that 

	the one on the right is more verbose and doesn't encapsulate the
functionality

	as well as the one the left. If the threads triggered by the
function are related

	to the function call --- for example, they may be associated
with the particular 

	transactor being created --- then the code on the right becomes
a exceedingly more 

	difficult to write: For example, the function may have to add
the transactor handle 

	to some dynamic data such as queue, which is then examined by
the threads.

	It's also important to remember that the code on the right
cannot be written in a 

	package, thus, making life even more difficult for both model
writers and users of 

	the model.

	 

	Forking threads from within a constructor is very common idiom
in both Vera and 

	C++ models. If we were to disallow this in P1800, I imagine a
lot of IP writers will 

	be upset. I suspect that these are the same people that have
been questioning 

	Dave on the availability of this feature. I'm not advocating
that SystemVerilog allow

	all types of fork/join within functions, only fork/join_none,
which is fundamentally

	not a time consuming construct.

	 

	Sorry for blasting this to both sv-bc and sv-ec, but I believe
it is important to let

	both committees know about this (sorry for the copies Cliff).

	 

	            Arturo

	 

	 -----Original Message-----
	From: owner-sv-ec@eda.org [mailto:owner-sv-ec@eda.org] On Behalf
Of Rich, Dave
	Sent: Friday, February 17, 2006 1:31 PM
	To: sv-ec@eda.org
	Subject: [sv-ec] RE: [sv-bc] Can a function contain a
fork/join/any/none?

	 

	I'm just going to copy the sv-ec from now on, since from the
perspective

	of the sv-bc, this only deal with issues related to the
testbench. The

	main place that I have seen this is in the new function for a

	constructor.

	 

	This snippet of code below was submitted to the SPIRIT working
group

	back in September. Since then I have seen it in a few other
places,

	basically the same.

	 

	class ahbram;

	   ahb_slv_if.comp sigs;

	   local ahbram_callbacks cbs[$];

	   local bit [1023:0] ram;

	 

	   extern function new(string          instance,

	                       ahb_slv_if.comp sigs);

	 

	   extern function bit [1023:0] peek(bit [31:0] addr);

	   extern function void poke(bit [  31:0] addr,

	                             bit [1023:0] data);

	 

	   extern function void register_callbacks(ahbram_callbacks cb);

	 

	   extern local task main();

	endclass: ahbram

	 

	 

	function ahbram::new(string          instance,

	                     ahb_slv_if.comp sigs);

	   this.sigs = sigs;

	   fork

	      this.main();

	   join_none    

	endfunction: new

	 

	 

	I would grant that if a fork/jone_none were legal in a function,
then I

	can see stretching the LRM to say that the task call inside the
fork is

	executing outside the function, or even stretching the LRM to
say that

	the "new" function is not really a function, its a constructor
and

	doesn't have to follow the rules of Verilog function.

	 

	Now we have the statement

	 

	ahbram R1 = new();

	 

	creating a thread. But that statement could be in a package, or
inside

	another function be called outside of a procedural context

	 

	Dave

	 

	 

	 

	> -----Original Message-----

	> From: owner-sv-bc@eda.org [mailto:owner-sv-bc@eda.org] On
Behalf Of

	Brad

	> Pierce

	> Sent: Friday, February 17, 2006 12:23 PM

	> To: sv-bc@eda.org

	> Cc: sv-ec@eda.org

	> Subject: Re: [sv-bc] Can a function contain a
fork/join/any/none?

	> 

	> Cliff,

	> 

	> That's not at all what I asked.  I don't see anything wrong
with using

	> fork-join in functions.

	> 

	> INSTEAD, I asked Dave, who started this thread, why so many
people are

	> asking him the question in his subject line.  My guess is that
they

	are

	> indeed seeing examples of this style and then asking Dave, "is
that

	> really legal?".

	> 

	> Yes, it's really legal.

	> 

	> How might they be seeing examples of this legal usage?  Maybe
the

	people

	> that are asking him are tool developers who are getting bug
reports

	> about this legal usage, in which case the need for backward

	> compatibility would be even stronger.  It would be interesting
to

	know.

	> 

	> -- Brad

	> 

	> -----Original Message-----

	> From: owner-sv-bc@eda.org [mailto:owner-sv-bc@eda.org] On
Behalf Of

	> Clifford E. Cummings

	> Sent: Friday, February 17, 2006 12:01 PM

	> To: sv-bc@eda.org

	> Subject: [sv-bc] Can a function contain a fork/join/any/none?

	> 

	> Hi, All -

	> 

	> Just the sv-bc on this message (I'm tired of getting two of
everyone

	> of these messages)

	> 

	> Brad asked the question and I think Stu hinted around the
question:

	> 

	> What is the use-model for adding fork-join to a function?

	> 

	> Are there any engineers doing this? (And has anybody slapped
them for

	> doing it?  :-) )

	> 

	> The engineers that ask this question, do they understand that
no

	> delays are allowed in a function (not even #0 delays or
nonblocking

	> assignments that cause events to be scheduled in later event
regions

	> within the same timestep) and that fork-join are generally not

	> synthesizable and are really most useful when you add some
form of

	> delay to the forked paths within a testbench?

	> 

	> I can't say for sure, but I don't believe either Stu or I have
ever

	> shown engineers a fork-join in a function in training and I
don't

	> think we ever intend to teach this coding style.

	> 

	> I understand that it might be important to add some form of

	> clarification to the LRM but whatever clarification we add
should

	> come with the strong editorial comment that this is/was a bad
idea.

	> 

	> I will support whatever the group goes for on this and then I
will

	> never teach an engineer to add fork-join to a function (unless

	> somebody can come up with some ultra-astonishing use-model
that I

	> have not heretofore recognized).

	> 

	> Regards - Cliff

	> 

	> ----------------------------------------------------

	> Cliff Cummings - Sunburst Design, Inc.

	> 14314 SW Allen Blvd., PMB 501, Beaverton, OR 97005

	> Phone: 503-641-8446 / FAX: 503-641-8486

	> cliffc@sunburst-design.com / www.sunburst-design.com

	> Expert Verilog, SystemVerilog, Synthesis and Verification
Training

	> 

	 

	 
Received on Sun Feb 26 08:31:25 2006

This archive was generated by hypermail 2.1.8 : Sun Feb 26 2006 - 08:33:05 PST