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
<instance_name>#<node_name>
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
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)
Examples
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