Subject: Re: [sv-cc] Re-proposed user data API
From: Francoise Martinolle (fm@cadence.com)
Date: Wed Mar 26 2003 - 12:50:33 PST
See my comments embedded.
At 11:55 AM 3/26/2003 -0800, Warmke, Doug wrote:
>John, Team,
>
>After reading John's comments here, a little more follow-up,
>and a night of sleeping on this, I believe that the current
>ID-based scheme is not usable or intuitive enough for the
>purpose of accessing user data.
>
>Andrzej's latest proposal seems like it will work, but the
>essence of it is "for some memory savings, put the complexity
>of managing instance-to-instance inter-language connections
>squarely on the user's shoulders". I think this is in
>opposition to the "simple but powerful" spirit of DPI.
>Please read on, for a very simple but powerful modification
>of John/Francoise's earlier work. Memory usage is *slightly*
>higher than Andrzej's in some cases, but the power and
>simplicity elements are much higher for the user.
>
>Let's back up and review the problem we are trying to solve.
>Basically, specific instances of C models want to be bound
>to specific instances of SV models.
>
>User data is used to manage this binding, since context isn't
>as explicit as is necessary, and implicit context isn't possible
>(as in pure C++) due to the two different language systems.
>
>Now to cut to the chase:
>Consider an SV module m, which declares three context import
>functions f1, f2, and f3. Consider a design which contains
>three instances of m: i1, i2, and i3.
>
>There are different ways to organize user data. The most flexible
>and useful is that each function declaration instance will store
>unique user data. This is just one pointer, not expensive. In our
>example design, there will be nine unique user data elements:
>
> i1.f1 i2.f1 i3.f1
> i1.f2 i2.f2 i3.f2
> i1.f3 i2.f3 i3.f3
>
>An important question is, "How do we work with these in the C API?"
>We need to put data into the storage areas, usually at init time.
>We need to get data out of the storage areas, at runtime, high speed.
>
>In this kind of 2-D storage scheme we need two keys to address the
>specific data that is needed. The first is svScope. That is already
>present in the API, and it is used to fetch up i1, i2, i3. The second
>is the name of the function. This second key has been the focus of
>our debates over the past days.
>
>Here is a summary of the approaches that have been considered
>for the 2nd key:
>
>1. String-based
> void svPutUserData(svScope instance, const char* funcName, void* data);
> void* svGetUserData(svScope instance, const char* funcName);
>
> This is too slow at runtime due to string comparison.
>
>2. Function scope handle based
> svFunctionScope svGetFunctionScope(svScope instance, const char*
>funcName);
> void svPutUserData(svScope instance, svFunctionScope function, void*
>data);
> void* svGetUserData(svScope instance, svFunctionScope function);
>
> Personally I don't see anything fundamentally wrong with this,
> and I think we should adopt this or some variant of it. (See #4 below)
>
> At one point, there was a problem regarding overlapping usage
> of function scope and instance scope. I agree that was confusing
> and it should be avoided.
>
> From what I understand, others have objected to the fact that all
> nine user data (in our example) need to have static storage allocated,
> even if they are not used. I don't think that is a big deal.
> Several points here:
> a) What needs to be stored is very small, just 32-bits
> b) This is only for context import functions, not all import functions
> c) There won't be a large number of instances of declarative scopes
> containing context import functions. Even 100 would be an
> aggressively large number. 400 bytes storage across the simulation
> is not a lot.
> d) There won't be a large number of context import function
>declarations
> in any given declarative scope. Once again, the multiplier effect
> that really hurts memory and performance is absent.
>
>3. ID-based scheme.
> I believe the motivation here is to only have "pay-as-you-go" storage
> requirements for user data. User data ID's are dynamically allocated
> on a request basis. Then they can be used to set and retrieve data
> with good performance.
>
> As we've seen there are several difficulties with this approach:
> a) John's example and my comments show that it is highly unintuitive
> b) John's subsequent mail about requiring "module name" in order
> to fetch a user data ID really has me lost. I think users will
> be more than lost when confronted with this kind of system.
>
>4. Hybrid ID - Function scope approach
> In a way, approach #2 (function scopes) is almost like an ID scheme.
> If we changed that approach to:
> int svGetFunctionID(svScope instance, const char* funcName);
> void svPutUserData(svScope instance, int functionID, void* data);
> void* svGetUserData(svScope instance, int functionID);
>
> Then we have an ID scheme, and there is no confusion with overlapping
> scope concepts. The only drawback is that it's not "pay as you go".
> But consider the mitigating points discussed in 2. above. This is
> already far better than VPI, in which user data must be accessible
> at every call site. There can be many more of those than context
> import function declaration scope instances.
>
> Now, an additional improvement became clear as I was recoding John's
> example below to make use of this approach. In most cases, the
> desired functionID is directly associated with the name of the
> context import function in which C code is running. Rather than
> requiring storage and retrieval of a functionID for such cases,
> we should reserve functionID 0 to denote the functionID of the
> currently executing context import function. This makes John's
> example below much simpler, as I hope you will agree.
I don't like the idea of using function id 0 to refer to the current
function ID.
Since the user requested the ID, he should remember it and use it instead
of putting the burden on the simulator to have to remember the ID of the
function
executing. In the later case, the simulator has to keep the association
between ID and
function_name/instance pair.
>*** In summary, I urge us to adopt approach 4. ***
>
>Here is John's example, recoded using approach 4.
>
>SV Side:
>
>[...]
>
>C Side:
>
>// Define the function and model class on the C++ side:
>class MyCModel {
> private:
> int locallyMapped(int portID); // Does something interesting...
>
> public:
> // Constructor
> MyCModel(const char* instancePath) {
> svScope scope = svGetScopeFromName(instancePath);
>
> // Allocate a unique ID to be used as key to associate
> // user data with "MyCFunc" in its module scope.
> int functionId = svGetFunctionID(scope, "MyCFunc");
>
> // Associate "this" with SV scope (avoids a hash in C++ code)
> svPutUserData(scope, functionID, this);
> }
>
> friend int MyCFunc(int portID);
>};
>
>// Implementation of context import function callable in SV
>int MyCFunc(int portID) {
> // Retrieve SV module instance scope (i.e. this function's context).
> svScope scope = svGetScope();
>
> // Retrieve and make use of user data stored in SV scope
> MyCModel* me = (MyCModel*)svGetUserData(scope, 0);
>
> return me->locallyMapped(portID);
>}
>
>
>Let's do a little analysis on what the example is actually doing
>and how it relates to the previous imaginary design I started with.
>Imagine that there are three instances of this C++ class. Each one
>is associated with a unique SV instance of the module which declares
>import function "MyCFunc".
>
>In this case, instead of a 3x3 matrix of user data, there is only
>a 1x3 matrix of data. The three user data storage areas will contain
>the three unique "this" pointers of the three C++ objects. Each cell
>of the matrix is addressed by (svScope, functionID). Probably all 3
>functionID's will be identical low integers, but that is up to the
>implementation and not important. As each C++ object is constructed,
>it fetches the appropriate svScope and functionID, then stores its
>"this" pointer into userData.
>
>It's easy to imagine extending this example to contain 3 separate
>C++ classes, each of which has a unique friend context import function
>declared in the same SV module. We would get to the original 3x3 matrix
>of user data that way. If it's not easy to imagine that, I would be
>happy to code up such an example, just let me know.
>
>Now imagine another scenario: One C++ class and one instance of that
>class, but 3 separate friend functions associated with 3 context import
>functions declared in the same SV module. In this case we would have a
>3x1 matrix. All three user data storage areas would have the same
>"this" pointer.
>
>Now a more complex scenario: Three C++ classes, three friend functions
>associated with each class (context import functions), and 1 instance
>of each class.
>Call the C++ "this" pointers ci1, ci2, and ci3.
>Call the SV instances i1, i2, i3, as before.
>Call the three context import (friend) functions f1, f2, f3, as before.
>Recall the "formal" 3x3 matrix entries are:
> i1.f1 i2.f1 i3.f1
> i1.f2 i2.f2 i3.f2
> i1.f3 i2.f3 i3.f3
>
>In this case the actual values would be:
> ci1 ci2 ci3
> ci1 ci2 ci3
> ci1 ci2 ci3
>
>
>Now another complex scenario: Three C++ classes, one context
>import (friend) function associated with each class, and 3 instances
>of each class. Each class instance is associated with one SV instance.
>Call the three classes c1, c2, c3.
>Call the "this" pointers of the classes c1i1, c1i2, c1i3, c2i1, etc.
>Call the context import (friend) functions f1, f2, f3.
>Call the SV instances i1, i2, i3.
>
>Then the matrix values fill out to:
> c1i1 c1i2 c1i3
> c2i1 c2i2 c2i3
> c3i1 c3i2 c3i3
>
>******
>
>Lots of such permutations are possible with approach #4.
>It is the most general, simple approach to user data storage.
>No "user layer" complexity is required, no verbose explanations
>in the LRM (or ancillary money-making follow-on docs ;) ), etc.
>And for a very acceptable price, in my opinion.
>Simplicity does have a price, and in this case I think
>we need to pay it for context import functions.
>
>Thanks and regards,
>Doug
>
>
> > -----Original Message-----
> > From: Stickley, John
> > Sent: Tuesday, March 25, 2003 10:22 PM
> > To: Warmke, Doug
> > Cc: sv-cc@server.eda.org
> > Subject: Re: [sv-cc] LRM modifications for svGet/PutUserData proposal
> >
> >
> > Doug,
> >
> > Warmke, Doug wrote:
> > > Hi John,
> > >
> > > Real nice work hammering out a compromise here.
> > > I have a couple of corrections to make in the example. Please see
> > > below.
> > >
> > > Regards,
> > > Doug
> > >
> > > > -----Original Message-----
> > > > From: Stickley, John
> > > > Sent: Tuesday, March 25, 2003 2:07 PM
> > > > To: sv-cc@server.eda.org
> > > > Subject: [sv-cc] LRM modifications for svGet/PutUserData
> > proposal
> > > > >
> > > > Team,
> > > >
> > > > In the interest of expediency and ease of insertion, I've >
> > > tried to frame Francoise's proposal in the form that can > be
> > > inserted directly into Joao's draft of the LRM. >
> > > > Note to Joao: There are a number of errors in the example
> > > > in section A.8.4. Please supersede with my version where those
> > > > errors have been corrected (and where the new get/putUserData()
> > > > usage is demonstrated). Also, what is missing is an
> > > > example of invoking an exported function after setting
> > > > svPutScope(). I can provide an extension to the existing
> > > > example that shows this if you wish.
> > > >
> > > > Here are the required updates to the LRM.
> > > >
> > > > ---------------------- cut here ----------------------
> > > >
> > > > A.8.3 Working with DPI context functions in C code
> > > >
> > > > [...]
> > > >
> > > > /* Return a unique ID that can be used as a key to store user
> > > > data in a given module scope. */
> > > > int svGetUserDataIdForScope(const svScope scope);
> > > >
> > > > /* Set arbitrary user data pointer into specified
> > instance scope */
> > > > void svPutUserData(const svScope scope, int userDataId, void* >
> > > userData); >
> > > > /* Retrieve arbitrary user data from specified instance scope */
> > > > void* svGetUserData(const svScope scope, int userDataId );
> > > >
> > > > [...]
> > > >
> > > > New section
> > > > | | | | |
> > > > V V V V V
> > > >
> > > > A.8.3.1 Associating User Data with Module Contexts
> > > >
> > > > The DPI allows association of multiple user defined data
> > > > pointers with module instances by allocating unique ID's
> > > > that can serve has keys to identify each distinct user
> > > > data pointer to be associated with a given module instance.
> > > >
> > > > The user data pointer is of type void * and is never
> > > > interpreted by the SV infrastructure - only by the user
> > > > application.
> > > >
> > > > The svGetUserDataIdForScope() function can be called at
> > > > initialization time to request unique IDs that can be
> > > > used to identify user data pointers to be associated
> > > > with a given module scope.
> > > >
> > > > The returned ID can be stored in a place known to the
> > > > C code (most likely a static or global variable) and
> > > > later be frequently referenced during run-time when
> > > > svGetUserData() is called to retrieve user data
> > > > pointers inside imported C functions that are called
> > > > from SystemVerilog.
> > > >
> > > > The functions svPutUserData() and svGetuserData() each
> > > > require a module scope handle and a user data ID argument.
> > > >
> > > > ID=0 is reserved to denote the NULL or invalid ID. This
> > > > allows a variable used to store an ID to be conveniently
> > > > tested for validity.
> > > >
> > > > A.8.4 Example 1 - Using DPI context functions
> > > >
> > > > SV Side:
> > > >
> > > > [...]
> > > >
> > > > C Side:
> > >
> > > DOUG: [Admittedly minor nitpick]
> > > We should use consistent whitespace here.
> > > The examples all use K&R whitespace, such as
> > > if (condition) {
> > > doSomething();
> > > }
> > >
> > > In this code, there are a few tidbits like:
> > > if( condition ){
> > > doSomething();
> > > }
> > >
> > > Let's clean those up in the LRM so all is consistent.
> >
> > johnS:
> > No problem - I can fix this. Hard to break old habits !
> >
> > >
> > > >
> > > > // Define the function and model class on the C++ side: > class
> > > MyCModel {
> > > > private:
> > > > static int dUserDataId;
> > > > int locallyMapped(int portID); // Does something
> > > > interesting...
> > > >
> > > > public:
> > > > // Constructor
> > > > MyCModel(const char* instancePath) {
> > > > svScope scope = svGetScopeByName(instancePath);
> > > >
> > > > // Allocate a unique ID to be used as key
> > to associate
> > > > // user data with module scope. Skip this step if ID
> > > > // has already been assigned.
> > > > if( dUserDataId != 0 )
> > > > dUserDataId = svGetUserDataIdForScope( scope );
> > > >
> > > > // Associate "this" with SV scope (avoids a
> > hash in C++
> > > code)
> > > > svPutUserData(scope, this);
> > > DOUG: You forgot to use the new second argument to this function.
> > > Should be:
> > > svPutUserData(scope, dUserDataId, this);
> >
> > johnS:
> > You are right. Thank-you.
> >
> > >
> > > Further, I'm not exactly clear on why you are using the
> > static class
> > > member rather than a normal class member. It doesn't seem to be
> > > reasonable, considering that this is not a static class.
> > If there is
> > > more than one instance of this class, the different
> > constructors will
> > > overwrite each other's user data with different "this"
> > pointers. The
> > > final constructed class instance will dominate the others.
> > >
> >
> > johnS:
> > Your confusion is certainly justified and is the key reason
> > I still am not completely comfortable with this interface
> > - mainly because I think users will be equally confused about
> > the subtlety of the requirement for static storage of the user ID.
> >
> > So let me try to explain. The ID itself cannot be stored in the user's
> > instance of the C model. This is because the ID must be used inside an
> > imported function to fetch the user's C model instance in the first
> > place (using svGetUserData()).
> >
> > The thing probably causing most of your confusion however
> > is that there's a flaw in passing the svScope into
> > svGetUserDataIdForScope().
> >
> > I just sent out an e-mail explaining that this function needs
> > to take moduleName rather than scope handle. This allows you
> > to obtain a single unique ID that is usable with *all*
> > instances of that module.
> >
> > Which leads to the issue of why a static is used. By storing
> > the ID as a static in the C model, the imported function can
> > fetch the access ID before actually having the C model
> > (a.k.a. user data) pointer. This is why static storage is
> > required here.
> >
> > It works but I fear it is non-intuitive. However, it does
> > addess Andrzej's and Joao's concern about the overhead of
> > having to set up a current function scope object each time an
> > imported function is called which my proposal of yesterday
> > would have required. With the ID scheme you avoid this. The
> > only thing that has to be set up is the current module scope.
> >
> > > Please see if you think I have misunderstood something, and if not,
> > > please double-check your intentions. If you do intend to
> > demonstrate
> > > using static class data to represent a "shared user data", then I
> > > think the comments or text surrounding the example should
> > express the
> > > intention in English language.
> > >
> > > Other than this confusion (quite possibly my own!), great job here.
> > >
> > > Regards,
> > > Doug
> > >
> > > > }
> > > >
> > > > friend int MyCFunc(int portID);
> > > > };
> > > >
> > > > int MyCModel::dUserDataId = 0;
> > > >
> > > > // Implementation of external context function callable in SV >
> > > int MyCFunc(int portID) {
> > > > // Retrieve SV module instance scope (i.e. this function's
> > > context).
> > > > svScope scope = svGetScope();
> > > >
> > > > // Retrieve and make use of user data stored in SV scope
> > > > MyCModel* me = (MyCModel*)svGetUserData( scope,
> > dUserDataId );
> > > >
> > > > return me->locallyMapped(portID);
> > > > }
> > > >
> > > >
> > __
> > ______ | \
> > ______________________/ \__ / \
> > \ H Dome ___/ |
> > John Stickley E | a __ ___/ / \____
> > Principal Engineer l | l | \ /
> > Verification Solutions Group | f | \/ ____
> > Mentor Graphics Corp. - MED C \ -- / /
> > 17 E. Cedar Place a \ __/ / /
> > Ramsey, NJ 07446 p | / ___/
> > | / /
> > mailto:John_Stickley@mentor.com \ /
> > Phone: (201)818-2585 \ /
> > ---------
> >
This archive was generated by hypermail 2b28 : Wed Mar 26 2003 - 13:52:20 PST