C
Netlist Compiled Functions (NCF)
The Spectre circuit simulator now allows a netlist expression to call functions that are loaded from a Dynamic Link Library (DLL).With this functionality, you can create your own functions in C or C++, for example, taking advantage of the features of these languages and overcoming the restrictions of the netlist user-defined function.
Loading a Plug-in
A plug-in can be loaded using either the -plugin command-line option or the CDS_MMSIM_PLUGINS environment variable. Both approaches allow the use of embedded environment variables, % modifiers and tilde expansion. A list of plug-ins can be provided using CDS_MMSIM_PLUGINS. Elements of the list are separated by whitespace or semicolons.
% spectre -plugin ~/plugins/%O/libmyplugin_sh.so mytest.scs +log %C:r.outor
% setenv CDS_MMSIM_PLUGINS "~/plugins/%O/libmyplugin_sh.so"
% spectre mytest.scs +log %C:r.out
-
%I: MMSIM installation hierarchy -
%O: Platform specified, equivalent to the result ofcds_plat -
%B: A 32-bit executable replaces this with an empty string. A 64-bit executable expands this to the string64bit.
Using a NCF in a Spectre Netlist
A NCF is called in a Spectre netlist just like any built-in mathematical function or user-defined function is called. There is no special syntax required to use a NCF once its plug-in has been successfully loaded by Spectre. In the following example, safe_sqrt( x ) is a simple NCF that evaluates the following code
if (x < 0.0)
return 0.0;
else
return sqrt( x );
In the Spectre netlist, this is called as follows
parameters w=1u y=safe_sqrt( w )
It could also be called on any instance or model parameter expression. There are some restrictions on the use of NCF functions.
- A NCF cannot be used in a behavioral source if an argument to the NCF is non-constant, i.e. a reference to a node voltage, device current, etc.
r1 1 0 resistor r=add( 1.0, 2.0 )*1k // Used correctly
b1 1 0 bsource i=v(1)/(add( 1.0, 2.0 )*1k) // Used correctly
b1 1 0 bsource i=add( v(1), 0 )/1k // Error, cannot compute d(i)/d(v(1))
Creating a Plug-in
You must include the ncf.h file to provide declarations of all functions and variables used in the plug-in.
The plug-in must contain the ncfinstall function, which must include a call to the ncfSetDefaultVersion version. This function informs the application the version of the NCF interface that the following NCF functions support. If the call to this function fails, the application prints an error message, and ignores all subsequent NCF calls from this plug-in.
A sample plug-in is given below.
#include <math.h>
#include "plugins/ncf.h"
double
foo( ncfHandle_t handle, int argc, double argv[] )
{
/* return result; */
}
void
ncfInstall( void )
{
ncfHandle_t func = 0L;
if (ncfSetDefaultVersion( NCF_VERSION_1 ) == ncfFalse )
return;
/* Create and register the function "foo" */
func = ncfCreateFunction( "foo" );
ncfRegisterFunction( func );
}
Assuming that SPECTRE_INSTALL is the root of the SPECTRE installation, the path to the ncf.h file is
${SPECTRE_INSTALL}/tools/mmsim/include/plugins
When adding this path to the compile line using the -I option, you can add the path to the mmsim/include directory, rather the path to the plug-ins directory, as follows:
% gcc -fPIC -I${MMSIM_INSTALL}/tools/mmsim/include -o myplugin.o -c myplugin.c
No Cadence libraries need to be linked to the final plug-in DLL, however it is normally necessary to link in the math library. To create the plug-in, all object files should be compiles with the appropriate PIC option and then linked together into the DLL. The following example uses gcc to create the shared library.
% gcc -shared -o libmyplugin_sh.so myplugin.o -lm
% cp libmyplugin_sh.so ~/plugins/`cds_plat`
Installing a NCF
To create a NCF, you must first call the function ncfCreateFunction. The only argument is the name of the NCF as it will be called from the netlist. The return value is a handle to the NCF object, a ncfHandle_t. If the call to ncfCreateFunction fails, a value of 0L is returned. You must then register the NCF with the application by calling ncfRegisterFunction. The only argument passed to this function is the previously created handle.
ncfHandle_t func = ncfCreateFunction( "foo" );
ncfRegisterFunction( func );
In the above example, the NCF foo is created and registered with the application. By default the NCF has the following attributes.
- It takes one scalar real argument which has pass-by-value semantics.
- Its return value is a real scalar.
-
The name of the function as called from the netlist is
foo. -
Since the developer has not provided a compiled function, the application searches the plug-in for an exported symbol with the name
foo, and assumes that symbol is the function to be executed when the NCF is called.
Modifying the Default Behavior of a NCF
While the default behavior of the NCF can be sufficient, this may not be the case. For example, the NCF may take more than one argument, the name of the compiled function is different than the name of the NCF, or the compiled function is not exported from the plug-in. The following functions can be used to modify the default behavior of the NCF.
ncfSetNumArgs( ncfHandle_t, int, int )
This function takes three arguments. The first is a handle to the NCF being modified. The next two are the minimum and maximum number of arguments, respectively.
ncfSetNumArgs( func, 2, 2 );
ncfSetNumArgs( func, 2, 10 );
In the first example above, the NCF func accepts two arguments. If the call to this NCF from the netlist has a different number of arguments, the parser will error out immediately. In the second example, the NCF can have any number of arguments from a minimum of two to a maximum of ten.
ncfSetDLLFunctionV1( ncfHandle_t, ncfFunctionV1Ptr_t )
If the you do not specify a compiled function for a particular NCF, the application searches the plug-in for a function with the same name as the NCF. If such a symbol does not exist or it exists but does not have global scope (in C this would indicate that it has static linkage), then an error message is printed and the application exits.
The function ncfSetDLLFunctionV1 can be used to specify the compiled function to be called when an NCF is evaluated. The V1 suffix indicates that the function conforms to the NCF_VERSION_1 interface. The first argument is a handle to the NCF being modified. The second argument is a pointer to the actual compiled function. For the NCF_VERSION_1 interface, the compiled function takes three arguments. The first is a handle to the function registered NCF, the second is the number of arguments in the netlist call, and the third is an array of double values. Each value corresponds to a value from the netlist call. The signature of the compiled function is
double (*ncfFunctionV1Ptr_t)( ncfHandle_t handle, int argc, double argv[] );
A simple example of the use of ncfSetDLLFunctionV1 is as follows
#include <math.h>
#include "plugins/ncf.h"
/* A simple function to add two arguments. */
static
double add( ncfHandle_t handle, int argc, double argv[] )
{
return argv[0] + argv[1];
}
void
ncfInstall( void )
{
ncfHandle_t func = 0L;
if (ncfSetDefaultVersion( NCF_VERSION_1 ) == ncfFalse)
return;
func = ncfCreateFunction( "add" );
ncfSetNumArgs( func, 2, 2 );
/* The call to ncfSetDLLFunctionV1 is required since 'add'
* is defined above to have static linkage, hence it cannot be
* seen by the application loading the plugin. */
ncfSetDLLFunctionV1( func, &add );
ncfRegisterFunction( func );
return;
}
Attaching Arbitrary Data to a NCF
You may wish to attach extra data to a NCF. This data can be attached during the creation of the NCF and then retrieved during function evaluation, using the provided ncfHandle_t argument. This feature is normally used to allow multiple NCFs to share a common for implementation or to provide a interposer type functionality.
ncfSetData( ncfHandle_t, ncfData_t )
ncfData_t ncfGetData( ncfHandle_t )
These functions set and get data on a previously created NCF. In the following example, two NCF, add and sub, are registered with the application, but they share a common compiled function implementation, add_or_sub. The compiled function uses the ncfGetData function to get the data associated with the supplied ncfHandle_t. If the data is +1, the supplied arguments are added: if the data is -1, the supplied arguments are subtracted. When the NCF’s are being created, you use the ncfSetData function to set data on each ncfHandle_t.
#include <math.h>
#include "plugins/ncf.h"
static double add_or_sub( ncfHandle_t handle, int argc, double argv[] ) { ncfData_t data = ncfGetData( handle ); if (data == +1 ) return argv[0] + argv[1];
else if (data == -1)
return argv[0] - argv[1];
else
return 0.0;
}
void ncfInstall( void ) { ncfHandle_t func = 0L; if (ncfSetDefaultVersion( NCF_VERSION_1 ) == ncfFalse) return;
func = ncfCreateFunction( "add" );
ncfSetNumArgs( func, 2, 2 );
ncfSetDLLFunctionV1( func, &add_or_sub );
ncfSetData( func, +1 );
ncfRegisterFunction( func );
func = ncfCreateFunction( "sub" );
ncfSetNumArgs( func, 2, 2 );
ncfSetDLLFunctionV1( func, &add_or_sub );
ncfSetData( func, -1 );
ncfRegisterFunction( func );
}
Return to top