››› Screenshot


Free Analog Circuit Simulation

Verilog-A compact model support

What is Verilog-A?

Verilog-A is a subset of Verilog-AMS. It is an industry standard modeling language for analog circuits. A Verilog-A device model is usually compiled into a shared library which can then be loaded by a simulator. The loaded model can then be used in circuit netlists.

OpenVAF is a Verilog-A compiler that builds shared library files which use the Open Source Device Interface (OSDI) to communicate with the simulator. SPICE OPUS supports OSDI version 0.3. At this point OP, DC, TF, AC, TRAN, and PZ analyses are supported. NOISE analysis support wll be implemented as soon as the OpenVAF compiler will support it (announced for 2023).

Building an OSDI file from Verilog-A sources

Suppose we want to build the following simple diode model stored in file spicediode.va

`include "constants.vams" `include "disciplines.vams" module simple_diode(A,C); // Simplified SPICE diode // Terminals and internal nodes inout A, C; electrical A,C,CI; // Branches branch (A,CI) br_a_ci; branch (CI,C) br_ci_c; // Model parameters (*desc = "Saturation current", units = "A"*) parameter real Is = 1e-14 from [0:inf]; (*desc = "Emission coefficient", units = ""*) parameter real N = 1 from [0:inf]; (*desc = "Ohmic resistance", units = "Ohm" *) parameter real Rs = 0.0 from [0:inf]; (*desc = "Reverse breakdown voltage", units = "V" *) parameter real BV = 1e20 from [0:inf]; (*desc = "Reverse breakdown current", units = "A" *) parameter real IBV = 1e-10 from (0:inf]; (*desc = "Saturation current temperature exponent", units = "" *) parameter real XTI = 3.0 from (0:inf]; (*desc = "Activation energy", units = "eV" *) parameter real EG = 1.12 from [0.1:inf]; (*desc = "Parameter extraction temperature", units = "C" *) parameter real Tnom = 27 from [-300.15:inf]; (*desc = "Zero-bias junction capacitance", units = "F"*) parameter real Cjo = 0.0 from [0:inf]; (*desc = "Junction potential", units = "V" *) parameter real Vj = 1.0 from [0.01:inf]; (*desc = "Grading coefficient", units = "" *) parameter real M = 0.5 from (0:0.9); (*desc = "Forward bias junction fit parameter", units = "" *) parameter real FC = 0.5 from [0:0.95); (*desc = "Transit time", units ="s" *) parameter real TT = 0.0 from [0:inf); // Instance parameters (*desc = "Device area factor", type = "instance", units = "" *) parameter real area = 1.0 from (0:inf); // Opvars (*desc= "Diode voltage", units ="V" *) real Vd; (*desc= "Diode ohmic current", units ="A" *) real Id; (*desc= "Diode charge", units ="As" *) real Qd; (*desc= "Differential conductance", units ="A/V" *) real gd; (*desc= "Differential capacitance", units ="F" *) real cd; // Adjustablje limited exponential analog function real lexp; input x, ylim; real x, ylim; real xlim; begin xlim = ln(ylim); if (x < xlim) begin lexp = exp(x); end else begin lexp = ylim*(x-xlim+1); end end endfunction // Variables real Vf, Vres, VT, Q; real expmaxf, expmaxr; real Tdev, Tnomk, EGTnom, EGT; real Vjeff, Cjoeff, F1, F2, F3; real Iseff, IBVeff, If, Ib; analog begin // Device temperature and parameter measurement temperature (in K) Tdev = $temperature; Tnomk = Tnom + 273.15; // Thermal voltage VT = `P_K*Tdev/`P_Q; // Branch voltages Vf = V(br_a_ci); Vres = V(br_ci_c); // Temperature and area adjusted saturation current Iseff = area * Is * pow(Tdev/Tnomk, XTI/N) * exp((Tdev/Tnomk-1)*EG/(N*VT)); // Forward exponential function limiting (at gd = 100/Rs) if (Rs>1e-3) expmaxf = 100/Rs*N*VT/Iseff; else expmaxf = 100/1e-3*N*VT/Iseff; // Ohmic current of diode branch If = Iseff * (lexp(Vf/(N*VT), expmaxf)-1); // Area adjusted breakdown current IBVeff = area * IBV; // Reverese exponential function limiting (at gd = 100/Rs) if (Rs>1e-3) expmaxr = 100/Rs*N*VT/IBVeff; else expmaxr = 100/1e-3*N*VT/IBVeff; // Breakdown current (at Vf=-BV the current Ib should be IBV) if ($param_given(BV)) begin Ib = IBVeff * lexp((-Vf-BV)/(N*VT), expmaxr); end else Ib = 0; // Junction charge EGTnom = 1.16-(7.02e-4*pow(Tnomk, 2))/(Tnomk+1108); EGT = 1.16-(7.02e-4*pow(Tdev, 2))/(Tdev+1108); Vjeff = Vj*(Tdev/Tnomk) - 3*VT*ln(Tdev/Tnomk) - (Tdev/Tnomk)*EGTnom + EGT; Cjoeff = area * Cjo * (1+M*(400e-6*(Tdev-Tnomk)-(Vjeff-Vj)/Vj)); if (Vf<FC*Vjeff) begin Q = If*TT + Cjoeff * Vjeff * (1-pow(1-Vf/Vjeff, 1-M))/(1-M); end else begin F1 = Vjeff * (1-pow(1-FC, 1-M))/(1-M); F2 = pow(1-FC, 1+M); F3 = 1 - FC*(1+M); Q = If*TT + Cjoeff * (F1 + (F3*(Vf-FC*Vjeff)+(M/(2*Vjeff)*(pow(Vf, 2)-pow(FC*Vjeff, 2))))/F2); end // Opvars (ddx(.,V(A))=ddx(.,V(br_a_ci)) Vd = Vf; Id = If - Ib; Qd = Q; gd = ddx(Id, V(A)); cd = ddx(Q, V(A)); // Diode branch current, add a small conductance gmin (for convergence) I(br_a_ci) <+ If - Ib + ddt(Q) + $simparam("gmin")*V(br_a_ci); // Resistor branch if (Rs <= 0) // Collapse CI and C if Rs is 0 V(CI,C) <+ 0; else I(CI,C) <+ Vres / (Rs/area); end endmodule

To build the OSDI module you will need the OpenVAF compiler. To save you the inconvenience both tested versions of the compiler (Windows and Linux) are available at the Download page.

The Windows version requires the MSVC linker from Visual Studio 2019 to be installed. It needs to be run in the x64 Tools Command Prompt for VS2019.

Once you have the compiler, type

openvaf simple_diode.va

A file named simple_diode.osdi will be created in the same directory. Now start SPICE OPUS and switch to the directory, where the .osdi file resides with the cd command. Then in the SPICE OPUS command prompt type

osdiload ./simple_diode.osdi

The model will be loaded. You can check that with the following command

siminfo devices

The loaded model can be found at the end of the output generated by SPICE OPUS. The osdiload command searches for the model in the default dynamic library search path followed by the SPICE lib/osdi directory. If you are loading an OSDI file from teh current directory, prefix its name with "./". A set of precompiled Verilog-A models is installed in the SPICE lib/osdi directory when you install SPICE OPUS. Once an OSDI model is loaded it cannot be unloaded. Therefore you need to restart the simulator and reload the model every time you change the Verilog-A source file and recompile it.

The osdiload command has the following syntax

osdiload [quiet|normal|verbose] <osdi_file>

The quiet option supresses all messages, while the verbose option prints out detailed information. If nothing is specified as the first option normal is assumed.

Parameters of OSDI devices

Detailed information about a device can be obtained with the devinfo command. Type

devinfo simple_diode

The output lists all terminals, internal nodes, model, and instance parameters of a device.

simple_diode: A simulator independent device with OSDI interface v0.3 Terminals: 2 1: A 2: C Internal nodes: 1 3: CI Collapsible node pairs: 1 (CI,C) Model parameters: $mfactor :RW--: real : is :RW--: real : Saturation current n :RW--: real : Emission coefficient rs :RW--: real : Ohmic resistance bv :RW--: real : Reverse breakdown voltage ibv :RW--: real : Reverse breakdown current xti :RW--: real : Saturation current temperature exponent eg :RW--: real : Activation energy tnom :RW--: real : Parameter extraction temperature cjo :RW--: real : Zero-bias junction capacitance vj :RW--: real : Junction potential m :RW--: real : Grading coefficient fc :RW--: real : Forward bias junction fit parameter tt :RW--: real : Transit time Instance parameters: $mfactor :RW--: real : vd :R---: real : Diode voltage id :R---: real : Diode ohmic current qd :R---: real : Diode charge gd :R---: real : Differential conductance cd :R---: real : Differential capacitance dt :RW--: real : Instance delta temperature temp :RW--: real : Instance temperature

SPICE OPUS changes the device name to lowercase so that Verilog-A modules with uppercase in their name are accessible in the simulator (the netlist is case-insensitive).

All Verilog-A parameter names are convered to lowercase. If the Verilog-A device does not have a parameter named temp an instance parameter with that name is addded by SPICE OPUS. When given this parameter overrides the circuit temperature for a particular instance. Similarly, if there is no Verilog-A parameter named dt an instance parameter with that name is added by SPICE OPUS. When given it specifies by how much the instance temperature is greater than the circuit temperature (or its override specified by the temp parameter). Both parameters are specified in degrees Celsius.

All Verilog-A instance parameters (with exception of parameters temp and dt added by SPICE OPUS) can be specified as model parameters. In this case they define the default value of the corresponding instance parameter.

The R flag means that the parameter can be read, while the W flag means that the parameter can be set. The P flag denotes principal parameters (e.g. the resistance of a resistor). Principal parameters can be found in builtin SPICE devices. The A flag denotes aliases (different name for a parameter that is listed before a sequence of aliases in the parameter list. The flags are followed by parameter type and parameter description.

All OSDI devices have an instance parameter named $mfactor. This is the multiplicity parameter (equivalent of the M parameter found in builtin SPICE devices).

Internal nodes of OSDI devices

OSDI devices can have internal nodes. In the diode example CI is an internal node. Internal node names are case sensitive. They are not converted to lowercase by SPICE OPUS. Because the NUTMEG interpreter is case sensitive they can easily be accessed as vectors named


OSDI devices can collapse internal nodes if the resistance between two nodes is small enough. In this way the CI node of the above example collapses into terminal C when the Rs parameter is 0. Collapsible node pairs are listed by the devinfo command.

Simulator parameters accessible in Verilog-A

Selected simulator parameters are accessible in Verilog-A models via the $simparam Verilog-A function. The OSDI interface in SPICE OPUS provides the following simulator options to Verilog-A models:

  • gdev is the value of the gmin simulator parameter
  • gmin is the greater of values gmin and gmindiag, where the latter one is the value added to the diagonal elements of the equation matrix during gmin stepping.
  • tnom is the value of the tnom simulator parameter
  • scale is the value of the scale simulator parameter
  • simulatorVersion is the version of the simulator
  • sourceScaleFactor is the value of source scaling factor during source stepping (between 0 and 1). Normally this parameter is 1.


Opvars are instance parameters that cannot be set. They can only be read. SPICE OPUS computes opvars during operating point analysis and transient analysis. Typically opvars are used for accessing differential conductances, differential capacitances, device charges and currents, etc. In the above example there are 5 opvars: vd (internal diode voltage), id (diode ohmic current), qd (diode charge), gd (differential conductance of the internal diode), and cd (differential capacitance of the internal diode).

Opvars can be stored by issuing save directives before the analysis is started. See the examples below for more information.

Change limiting functions

Change limiting functions limit the change of a quantity between consecutive iterations of the Newton-Raphson algorithm. These functions are built into SPICE and are used by its builtin models to improve convergence. They return the limited value of the given quantity and prevent the simulator from stopping the Newton-Raphson iteration before the change becomes small enough. They are available in Verilog-A via the $limit function with the following syntax.

$limit(quantity, function_name, ...)

Quantity can be any branch voltage or current. The function name must be quoted. The following functions are available

  • pnjlim limits the voltage change across pn junctions. Two extra arguments need to be specified. vt is the thermal voltage. vcrit specifies the voltage above which limiting takes place. Changes above vcrit are limited when they exceed 2vt.
  • typedpnjlim same as pnjlim, except that the quantity is multiplied by the third extra argument type before pnjlim limiting takes place.
  • limvds limits the drain-source voltage change for FET transistors. No additional parameters are required.
  • fetlim limits the gate-source voltage change for FET transistors. One additional parameter is required specifying the threshold voltage vto.
  • limitlog logarithmically limits the change of a quantity if it exceeds the value given by additional parameter LIM_TOL.

Specifying OSDI devices in netlists

An OSDI device name must start with N. The rest is the same as with builtin SPICE devices. The model type is the module name from the Verilog-A file converted to lowercase. To create a diode based on the above example one should write

n1 (1 0) dmod area=1.0 .model dmod simple_diode is=1e-12 n=2 rs=1u + bv=8 ibv=1e-8 + cjo=100p vj=1.0 m=0.5

Not all terminals of a device must be connected. If the last n terminals are not specified in the netlist they remain unconnected. Whether a terminal is connected can be detected in Verilog-A code with the $port_connected function.

A netlist containing an OSDI instance is successfully parsed if the corresponding OSDI file is loaded before the netlist is loaded into the simulator. This means that you have to either load the osdi file manually before invoking the source command or load it at simulator startup by specifying the corresponding osdiload command in the spinit file.

There is, however, a third option. You can load an osdi file with the .osdiload netlist directive. If the osdi file is already loaded, nothing happens. If, however, the file has not been loaded yet, it will be loaded before the netlist is parsed. The syntax of the .osdiload netlist directive is the same as the syntax of the osdiload NUTMEG command. The only difference is the way the simulator searches for the osdi file. The following search order is used

  • the directory where the file invoking the .osdiload netlist directive is located
  • the default dll search path
  • SPICE lib directory (lib/osdi)


The following is a complete example demonstrating OP, DC, AC, and TRAN analysis of an OSDI device. It also demonstrates accessing opvars via the show command in OP analysis and save directives in DC and TRAN analysis

Verilog-A diode test - OP, DC, AC, and TRAN .osdiload spicediode.osdi n1 (1 0) dmod area=1.0 v1 (1 0) dc=0.5 ac=1 pulse=(-10 2 0 20) .model dmod simple_diode is=1e-12 n=2 rs=0.1 + bv=80 ibv=1e-8 + cjo=100p vj=1.0 m=0.5 .control destroy all delete all echo Operating point analysis op print all show n1 : all save all @n1[gd] @n1[cd] dc v1 -82 2.0 lin 1000 plot -i(v1) yl -1 1 ylabel "Id [A]" xlabel "V(1) [V]" + title "I/V characteristic of a diode" plot @n1[gd] ylog xl 0.5 2 ylabel "gd [A/V]" xlabel "V(1) [V]" + title "Differential conductance" plot @n1[cd] ylog ylabel "cd [F]" xlabel "V(1) [V]" + title "Differential capacitance" * Do not save opvars in AC analysis (they do not change) delete all * Internal resistance and diode form an mpedance voltage divider. * Compute the frequency dependence of its ratio. ac dec 10 1 100g plot db(v(1)-n1#CI) xlabel "f [Hz]" ylabel "V(A,Ci)/V(1) [dB]" + title "Internal vs external diode AC voltage" * Ramp diode voltage from -10V to 2V slowly (in 20s). * Plot the differential capacitance. save all @n1[gd] @n1[cd] * First point of transient analysis is incorrect tran 1m 20 plot @n1[cd] vs v(1) ylog ylabel "cd [F]" xlabel "V(1) [V]" + title "cd from transient analysis, bad first point" * Skipping the first point of transient analysis tran 1m 20 0.1u plot @n1[cd] vs v(1) ylog ylabel "cd [F]" xlabel "V(1) [V]" + title "cd from transient analysis, skipped bad point" .endc .end

The first point of transient analysis is not valid (opvars are computed incirrectly). To get the correct results we skip the first point by setting start time in transient analysis.

The second example demonstrates the reverse recovery effect of the diode.

Verilog-A diode test - reverse recovery .osdiload spicediode.osdi n1 (1 0) dmod area=1.0 r1 (10 1) r=50 v1 (10 0) dc=0 pulse=(50 -50 1u 10n) .model dmod simple_diode is=1e-12 n=2 rs=0.1 + bv=80 ibv=1e-8 + cjo=100p vj=1.0 m=0.5 + tt=1e-6 .options method=gear .control destroy all delete all tran 10n 2u plot -i(v1) xlabel "t[s]" ylabel "i[A]" + title "Diode reverse recovery" .endc .end