Subject: Extended (dynamic) linking
From: Kevin Cameron x3251 (Kevin.Cameron@nsc.com)
Date: Tue Nov 26 2002 - 13:13:20 PST
[http://www.eda.org/sv-cc/hm/att-0380/
<http://www.eda.org/sv-cc/hm/att-0380/>]
The basic (C) linking functionality of my proposal is the same as John's
except that
I don't have an optional context on calls from C to SV (it's always the
pli handle of
the instance - which may be $root). The use of '*' in extern argument
lists does not
imply that SV supports pointers in general, only that it understands
what a C call's
interface is (it would be nice to use the same definitions as C so that
there is no
need to copy and edit), once the EC agrees on how to do references we
can finalize
that syntax (noting references are part of the testbench donation, but
the syntax is
inconsistent).
The "extended" linking behavior allows linking of foreign languages that
do not
have standard C entry points e.g. C++ which mangles names so that
routines have
a unique entry-point for each (overloaded) argument set. Since the name
mangling
in C++ is different for different compilers it is difficult to predicate
what the "C"
name should be for binding at link time. The sequence for dynamic
linking (with VCS)
is something like:
Compile Verilog:
Compiler will note external calls.
Link code:
Use a C++ linker if foreign code is C++ (SystemC)
Run simulation:
C++ static constructors get called for permanent data (see test
below),
register binders.
Simulator main() gets called.
Elaborate design. Call "binder" function for foreign routines as
encountered
(per instance) or post elaboration.
Start simulation.
The "binder" functions may be C++ functions (name mangled) since they are
passed through the C registration interface as pointers. When the binder
functions
are called they can use the call arguments to ensure an exact match between
SV and C and to select between multiple (overloaded) versions of calls.
Since each call is bound by call instance (file,line,position,module
instance and
arguments) there is the opportunity to tune each call while binding and
remove
run-time decision making. A call bound by the linker has to decipher which
instance it is and find it's context from there on at least the first
call, and although
subsequent calls have the right context, they still have to check it is
valid and
switch on the context information. If what you want to do is bind a C++
class
instance and method to the call then implementations will be something like
the following:
extern void cpp_method(int); // SV decl.
Will map to the call:
cpp_method(ip,p_context,data); // C binding
(*cpp_method_ptr)(ip,p_context,data); // dynamic binding
NB: routines in shared libraries are usually indirect calls even if
linked directly
so the difference between these methods is marginal for standard library
calls.
The directly linked call would be handled by a routine like:
void cpp_method(handle ip,svcContext *p_context,int data)
{
shadow_class *cp = (shadow_class *)p_context.user_context.ptr;
if (!cp) {
cp = mapInst2Shadow(ip,&p_context.user_context.ptr);
}
cp->method(data); // call method (virtual)
}
Which doesn't look too inefficient but the routine can't tell which
sub-class of
shadow_class it has if they vary by instance, so "method" is likely to be a
virtual function which is called indirectly via a virtual function table.
The dynamically bound routine can skip the context test (which is
handled during
binding) and can find a per-instance version of cpp_method so the code
is like:
void cpp_method_2(handle ip,svcContext *p_context,int data)
{
shadow_class_2 *cp = (shadow_class_2
*)p_context.user_context.ptr;
cp->method(data); // call method (direct)
}
Since the class shadow_class_2 is specific to the instance "method" does not
have to be virtual.
Calling SV from C is much the same in reverse.
So the benefits of the dynamic binding mechanism are a) handles non-C
foreign languages, b) arguments can be checked for compatibilty, c) more
efficient run-time calling with instance specific functions, d) doesn't add
exported names to C name space (less chance of a name clash).
Regards,
Kev.
------------------
C++ linker test:
#include <stdio.h>
static char str[] = "Goodbye"; // linker allocates and initializes
class foo {
public:
foo() {strcpy(str,"Hello");}
};
foo f1; // linker allocates space, constructor gets called before main.
int main()
{
return printf("%s\n",str); // should print "Hello"
}
-- National Semiconductor, Tel: (408) 721 3251 2900 Semiconductor Drive, Mail Stop D3-500, Santa Clara, CA 95052-8090
This archive was generated by hypermail 2b28 : Tue Nov 26 2002 - 13:16:55 PST