Adding a device to Xyce through Verilog-A
This section assumes you are already familiar with the Verilog-A language and are comfortable developing compact models in this language. This page does not include any Verilog-A instruction.
In this tutorial, we will add a simple two-terminal device to Xyce that internally simulates a resistor, capacitor, and inductor in series. When we're done, this device can be accessed in a netlist using the Xyce "Y device" extension:
YRLC <name> <PositiveNode> <NegativeNode> R=<resistance> L=<inductance> C=<capacitance>
Before you can use Verilog-A, you must download and build the ADMS code generator. Download the ADMS source code from https://github.com/Qucs/ADMS. This github site is the current repository for the actively maintained version of ADMS, which has already had patches proposed by the Xyce team applied to it. In prior releases we had recommended obtaining ADMS from an unmaintained sourceforge repository and applying patches to it, but that is no longer recommended.
Build ADMS according to the instructions provided with it.
The Xyce/ADMS capability is a work in progress
The Xyce team has been using the ADMS procedure outlined in this tutorial for several years, and has used it to add several devices to Xyce, including the following models: VBIC 3T_et_cf, FBH HBT_X 2.1, PSP, EKV 3.0, EKV 2.6, and BSIM-CMG 107. Nevertheless, the back-end described here is very much a work in progress and all of the models mentioned have required a lot more work than the simple RLC model does. The RLC model described below can be imported into Xyce just by running ADMS, adding the generated code to the Xyce build process, and recompiling.
Several issues add complexity to the task of getting a sophisticated Verilog-A model into Xyce:
- Node collapse, where an internal node may be mapped away based on the value of a model parameter
- Voltage limiting via "pnjlim" or other means
- Setting initial junction drops (as is usually required for BJT and HBT models)
- Complex use of analog functions
So far, the team has been able to improve the back-end to the point that it is not especially difficult to deal with these issues, but each one of them presents challenges to the back-end that almost always force us to do post-processing of the generated code before it will compile. If you are developing Verilog-A models that make use of these features, you may have to do some extra work (e.g., hand-editing or making patches) after ADMS is finished generating code. As an example, if you look in the utils/ADMS/examples/fbh_hbt-2.1, you will find three ".patch" files that post-process that device's analog function template definitions so that they will compile, and add junction voltage initialization.
The RLC device in Verilog-A
The simplest implementation of the series RLC device is given below.
`define attr(txt) (*txt*)
module rlc (p,n) `attr(xyceSpiceDeviceName="RLC" xyceLevelNumber="1");
electrical internal1, internal2;
parameter real L=1e-3 from (0:inf) `attr(info="Inductance" type="instance");
parameter real R=1e3 from (0:inf) `attr(info="Resistance" type="instance");
parameter real C=1e-12 from (0:inf) `attr(info="Capacitance" type="instance");
CapacitorCharge = V(internal1,internal2)*C;
I(internal1,internal2) <+ ddt(CapacitorCharge);
V(internal2,n) <+ L*ddt(InductorCurrent);
This simple example is not the optimum implementation of the RLC, but demonstrates some basic requirements. The above Verilog-A code may be found in the "utils/ADMS/examples/rlc.va" file included in the Xyce source code distribution.
In this example, the resistor, capacitor, and inductor are implemented exactly as they would be if they were stand-alone devices. A better example of how to implement this device, resulting in one fewer solution variable being required, is included in the Xyce source code as file "utils/ADMS/examples/rlc2.va".
Things to note about the example
- The first two lines are required for ADMS to include some definitions we need.
- The third line defines an "attr" macro that we use in the Xyce/ADMS back-end to tell Xyce how to use the device. This information cannot be encoded in Verilog-A directly and cannot be guessed. Some other simulators that use ADMS have information like this hard coded in their back-ends instead of using the technique here; but by doing it this way Xyce is able to import new Verilog-A models without having to edit the back-end code (unless the new model uses features not supported in the existing back-end).
- The Verilog-A module declaration "module rlc(p,n);" has been augmented with a call to the "attr" macro. The xyceSpiceDeviceName="RLC" attribute tells Xyce that when the model is compiled, it will be accessed as a device of type "YRLC". The xyceLevelNumber="1" is in this case not used, but it can be used to create multiple devices of the same type, selectable by having a model card with a "level" parameter to distinguish them. If these attributes are left out then the Xyce/ADMS back-end emits placeholder code with the comment "FIXME" in several places, and these can be hand-edited to add the appropriate information as a post-processing step. Using the attributes, however, is more convenient.
- Each parameter declaration has been augmented with attributes using the "attr" macro. "info" is used for documentation of the parameter (Xyce can output a LaTeX table containing device parameters including this information). "type" can be "instance" or "model", to declare that a parameter will occur either on an instance line or in a .model card. There are numerous other Xyce-specific attributes that can also be set. See the examples in the "utils/ADMS/examples" directory to see how they may be used in real devices such as the VBIC 3T_et_cf model or the FBH HBT_X model.
Processing the Verilog-A with ADMS
Once your Verilog-A file is created, it is necessary to use the Xyce/ADMS back-end with ADMS to generate C++ code that can be used in Xyce. Assuming your RLC code is saved in a file called "rlc.va", and that your Xyce source code tree is in $HOME/Xyce-6.7, this is accomplished by running "admsXml" (installed by ADMS) with command line arguments to point at the files in the Xyce/ADMS back-end.
admsXml -e $HOME/Xyce-6.7/utils/ADMS/xyceVersion.xml \
-e $HOME/Xyce-6.7/utils/ADMS/xyceBasicTemplates.xml \
-e $HOME/Xyce-6.7/utils/ADMS/xyceHeaderFile.xml \
-e $HOME/Xyce-6.7/utils/ADMS/xyceImplementationFile.xml \
As when we built Xyce, we have used the shell "continuation character" to break this single command line into two lines. It is important that "\" be the last character on the line, that it be preceded by a space, and that no spaces follow it.
The admsXml command line above will create a .C and a .h file ready to compile in to Xyce. The name of the files is based on the name of the device in the Verilog-A "module" line, and NOT the file name of the Verilog-A code itself. In the current implementation, the file name is constructed by appending "N_DEV_ADMS" to the module name. In the case of the RLC device, our output files are N_DEV_ADMSrlc.C and N_DEV_ADMSrlc.h.
To use this code in Xyce, it will be necessary to add it to the build system of Xyce, add a call to the new device's "registerDevice" method in an appropriate place, and recompile Xyce. These steps are discussed below.
"Wiring in" the ADMS-generated code
The ADMS command line given in the previous section created two C++ files, a header and an implementation file named "N_DEV_ADMSrlc.h" and "N_DEV_ADMSrlc.C", respectively. Because the RLC device is so simple, the code produced by ADMS is ready to insert into Xyce with no further modification. All that is necessary is to add the two files to the Xyce build process and to add code to the device manager to get Xyce to recognize the new device.
Add the RLC device to the build system
There is already an "ADMS" subdirectory of the src/DeviceModelPKG directory that is used for adding new devices produced by ADMS. Copy N_DEV_ADMSrlc.h to the src/DeviceModelPKG/ADMS/include directory, and copy N_DEV_ADMSrlc.C to the src/DeviceModelPKG/ADMS/src directory.
Edit the file "Makefile.am" in src/DeviceModelPKG/ADMS, and add the names of the two files to the "libADMS_la_SOURCES" variable, which should look like this when you are done:
libADMS_la_SOURCES = \
Remember to have no spaces after any "\" character above.
Register the new device
Edit the file "N_DEV_RegisterADMSDevices.C" in the src/DeviceModelPKG/ADMS/src directory. You will see a block of "#include" directives. Add your new device's include file here so it will now read:
Next, look a few lines down in the file and you'll see a series of calls to "registerDevice();" Add your new device's registerDevice call to this, so it reads:
Your device is now set up to be used. All that's left is to update the build system so that the new device is compiled, and rebuild Xyce.
Updating the build system
To rebuild the Xyce build system, you need the following tools:
If you don't have these tools installed on your system, you must install them before proceeding. Most operating systems provide these tools in their package management systems, or you can build them from source using the links above.
Once you have the autotools installed, you can execute the "bootstrap" script in the top level of the Xyce source tree:
Xyce is now ready to be recompiled. You will need to change directories to your Xyce build directory and re-run the "configure" invocation you used when building Xyce originally. Once configure is done, a "make" in the build directory will compile your new device and wire it in to Xyce.
You can check that the new device is there by running "src/Xyce -param | less" in the build directory. Xyce will respond by emitting the instance and model parameters recognized by all devices, and you can see your new device in there as "RLC level 1".
Testing the Verilog-A imported device
The RLC device you just created and wired in to Xyce can now be run in a netlist.
Because the SPICE netlist format recognizes device types by the first character on the line, there is a limited number of device types that can be supported using the normal netlist syntax. Xyce has opted to use the "Y" device as an extension mechanism. In this mechanism, rather than having a device's type and name specified as the first word on the line, the device type is taken from the characters following "Y" up to the first space, and the device name is the second word on the line.
By using the particular set of attributes on module declaration of the Verilog-A source, we have indicated that our new device will be known as a "YRLC" device. It will be used in a netlist using the following format:
YRLC <name> <node1> <node2> R=<resistance> L=<inductance> C=<capacitance>
Compare this to how a resistor would be specified:
R<name> <node1> <node2> <resistance>
A complete netlist that uses the new device is:
Test of series RLC circuit
V1 1 0 SIN (5v 5v 20MEG)
Yrlc rlc1 1 0 R=1kohm L=1mH C=1pf
.tran 1n 4u
.print tran v(1) I(v1)
This netlist will produce the same signals as the one given below, where the R, L, and C are discrete devices:
Test of series RLC circuit
V1 1 0 SIN (5v 5v 20MEG)
R1 1 2 1Kohm
C1 2 3 1pF
L1 3 0 1mH
.tran 1n 4u
.print tran v(1) I(v1)
Alternate approach: run-time dynamic library plug-in
In Xyce the most established method of adding a Verilog-A model is the one we have described up to now. But starting with the 6.1 release we have begun to implement a way of loading new devices using dynamically loaded shared libraries, rather than wiring them in at compile time. This feature is experimental and currently works only on real Unix-like systems like Linux, FreeBSD, and Mac OS X. It does not yet work on Windows under Cygwin.
If you want to try this method out, be sure to undo any changes you have made to Xyce to hard-wire the RLC device according to the directions above!
To use this feature you must build the shared-library version of Xyce. Once the shared-library build of Xyce is complete, you can build your new device as a shared library.
Rebuild Xyce to use shared libraries
The first step to using dynamic plug-ins is to rebuild Xyce to support the feature. Add the following two lines to the "configure" command line you used to build Xyce originally:
Once configure is re-run with these additional arguments, recompile Xyce with "make clean && make". The result will be that Xyce will be linked against a shared-library "libxyce.so" (or libxyce.dylib on OS X), and the user plugin option will be available.
Create the demo plug-in.
In your Xyce source directory you will find that there is a "user_plugin" directory that just happens to contain exactly the "rlc.va" Verilog-A model we have used above, and in your build directory you will find user_plugin directory that contains a Makefile that is all ready to use. From the top level of the build directory, after building Xyce, simply type "make plugin".
The build system will run ADMS on the Verilog input, compile the code, and produce a "libADMSrlc.so" file in the user_plugin/.libs directory.
You can then run "src/Xyce -plugin `pwd`/user_plugin/.libs/libADMSrlc.so -param" and see that the RLC device is present. If you omit the "-plugin" option and the shared library name, you will see the RLC device is not there. You can use Xyce with the "-plugin" option and shared library name to run the example netlist above, producing the same results.
If you install the shared build of Xyce with "make install", it will put the shared libraries into the lib directory associated with your install prefix. If you do so and want the plugin accessible, you should also do a "make install-plugin", which will put the shared library of the plugin in the same place. Xyce will not automatically detect the presence of plugin libraries in its lib directory --- you will always need to use the "-plugin" flag to tell Xyce which plugin to load.
Using a different device as a plugin
As shipped, the user_plugin directory is intended merely as a demonstration of how to create a shared library plugin. There are three essential components to this plugin directory:
- A Makefile.am file that declares the name of the library to produce and the files needed to produce it.
- The relevant Makefile.am is the one in the "user_plugin" subdirectory of the Xyce source tree.
- This Makefile.am also contains the ADMS command line to produce C++ from the verilog input.
- If you ever edit this file, you will need to re-run the "bootstrap" script in the top-level of the Xyce source tree, as you had to do when wiring in the device by hand.
- After running "bootstrap" you will also need to re-run "configure" in your build directory.
- A "N_ADMSrlc_bootstrap.C" file that declares a structure called "Bootstrap", whose constructor calls the "registerDevice()" method of the new device
- When Xyce is started with the "-plugin" option, it will load the shared library and create this Bootstrap object. The constructor of the object will automatically register the device, just as we did by hand when we added the "registerDevice()" call to the N_DEV_RegisterADMSDevices.C file, above.
- The Verilog-A input file, rlc.va
- This file will be processed into C++ by ADMS.
If you wanted to generate a plugin for a different device, you'd have to modify the Makefile.am file to compile your device instead. It is well commented and the files are named intuitively, so it should be reasonably easy to understand how to modify it. You would also need to produce a "bootstrap" file similar to the one provided for the RLC device.
Some things to note about the user_plugin directory if you are planning to modify its contents for your own device:
While the user_plugin directory is set up to use a Verilog-A model and ADMS to convert the Verilog-A to C++, you can also use this technique if you are writing a device from scratch in C++. You will have to modify the Makefile.am accordingly.
- The RLC device is very simple and is entirely contained in one file.
- If you are trying to build a more complex Verilog-A model, you might have to add additional options to the ADMS command line in user_plugin/Makefile.am
- The most likely thing you would have to add to the ADMS command line is an option to let it find any files that your main ".va" file includes. For example, if your top-level Verilog file includes several files in the same directory, add "-I$(srcdir)" to the admsXml command line.
- You will have to create a bootstrap C++ file similar to the one in N_DEV_ADMSrlc_bootstrap.C, customized to reference your own model's registerDevice method.
- The Xyce/ADMS back-end will create a class whose name is the concatenation of your "module" name and the letters "ADMS." Our RLC model had a line "module rlc" at the beginning, and so its name is "ADMSrlc"
- Your bootstrap file needs to include the .h file generated by ADMS, and must define a bootstrap struct whose constructor calls your registerDevice.
- The N_DEV_ADMSrlc_bootstrap.C file could be copied, replacing all the instances of "rlc" with the name that appears on your device's module line.
- You will need to rewrite the user_plugin/Makefile.am file in the Xyce source tree to refer to your own files instead of our demonstration files. Copying the existing Makefile.am, but changing "rlc" everywhere in that file to the module name of your device should be enough, unless you need to add a "-I" flag as described above.