NGSpice, the Open Source Spice Simulator
ngspice is a text-mode circuit simulator that numerically solves equations describing (electronic) circuits: These are made of passive and active devices. Time varying currents and voltages are simulated as well as noise and small signal behavior.
Ngspice is an open source update of Spice3f5, the last Berkeley's release of Spice3 simulator family. Ngspice is being developed to include new features to existing Spice3f5 and to fix its bugs. Improving a complex software like a circuit simulator is a very hard task and, while some improvements have been made, most of the work has been done on bug fixing and code refactoring.
On a linux (here Ubuntu) distribution several packages are available:
- ngspice: the executable, man pages and some documentation at /usr/share/doc/ngspice/, containing mainly an examples subdirectory (/usr/share/doc/ngspice/examples)
- ngspice-doc: in directory /usr/share/doc/ngspice-doc/html are found one large HTML file (/usr/share/doc/ngspice-doc/html/manual.html, 4.2MB), a compressed PDF version of the manual (/usr/share/doc/ngspice-doc/manual.pdf.gz), and sundry image files presumably used by manual.html.
There is a detailed reference manual available for ngspice. This manual describes all commands and procedures available in ngspice and lists numerous examples. However, it is not an ngspice how-to or introductory text. This tutorial here gives you some information how to start. If you are interested in getting more in-depth information, you may refer to our book page or to a list of third party tutorials.
Installing
Linux Installing
If you are using LINUX, please check if your distribution already offers a ngspice package for installation. If not, you will need to download the latest ngspice-XX.tar.gz at https://sourceforge.net/projects/ngspice/files/ng-spice-rework/ and compile ngspice with
./configure --with-x --enable-xspice --enable-cider\ --with-readline=yes --enable-openmp --disable-debug\ CFLAGS="-m64 -O2" LDFLAGS="-m64 -s" make -j8 sudo make install
or by running the script compile_linux.sh from folder ngspice/ by
./compile_linux.sh 64
An Example/Procedure
As shown in Fig. 1, You start with a circuit (here: an inverter). You have to create a netlist describing this circuit. The netlist is the input to ngspice, telling it about the circuit to be simulated. Together with some simulation commands this input cares for reading and parsing the netlist, starting the simulation and plotting the output. The output voltage (plotted in red) is the inverse of the (green) input. Both voltages are plotted versus time.
The input to ngspice is read from a file (either at the command line, as in ngspice vdiv.cir or at the ngspice prompt with command source, as in ngspice 1 -> source ./vdiv.cir), and it may be enhanced by commands given on the command line. The simulated output may be written to a file, or be plotted as a Y-X graph or a smith chart. There is no graphical user interface with schematic capture of circuit diagrams and automatic netlist generation.
Next we shall be looking at a simple example, a Circuit with Passive Elements, Operating Point.
Supported Analyses*
Circuit Description
General Structure and Conventions
The circuit to be analyzed is described to ngspice by a set of element lines, which define the circuit topology and element values, and a set of control lines, which define the model parameters and the run controls. All lines are assembled in an input file to be read by ngspice.
Two lines are essential:
-
The first line in the input file must be the title, which is the only comment line that does not need any special character in the first place.
-
The last line must be
.end(mind the leading period).
The order of the remaining lines is arbitrary (except, of course, that continuation lines must immediately follow the line being continued).
Circuit Elements
Each element in the circuit is specified by an element line that contains:
- the element name,
- the circuit nodes to which the element is connected, and
- the values of the parameters that determine the electrical characteristics of the element.
The first letter of the element name specifies the element type. For example, a resistor name must begin with the letter R and can contain one or more characters. Hence, R, R1, RSE, ROUT, and R3AC2ZY are valid resistor names. Details of each type of device are supplied below.
Fields on a line are separated by one or more blanks, a comma, an equal (=) sign, or a left or right parenthesis; extra spaces are ignored. A line may be continued by entering a +
(plus) in column 1 of the following line; ngspice continues reading beginning with column 2. A name field must begin with a letter (A through Z) and cannot contain any delimiters. A number field may be an integer field (12, -44), a floating point field (3.14159), either an integer or floating point number followed by an integer exponent (1e-14, 2.65e3), or either an integer or a floating point number followed by one of the following scale factors:
| Suffix | Name | Factor |
|---|---|---|
| T | Tera | 1012 |
| G | Giga | 109 |
| Meg | Mega | 106 |
| K | Kilo | 103 |
| mil | Mil | 25.4 × 10−6 |
| m | milli | 10−3 |
| u | micro | 10−6 |
| n | nano | 10−9 |
| p | pico | 10−12 |
| f | femto | 10−15 |
Letters immediately following a number that are not scale factors are ignored, and letters immediately following a scale factor are ignored. Hence, 10, 10V, 10Volts, and 10Hz all represent the same number, and M, MA, MSec, and MMhos all represent the same scale factor. Note that 1000, 1000.0, 1000Hz, 1e3, 1.0e3, 1kHz, and 1k all represent the same number.
Node names may be arbitrary character strings and are case insensitive. The ground node must be named 0
(zero). For compatibility reason gnd
is accepted as ground node, and will internally be treated as a global node and be converted to 0
.
Each circuit has to have a ground node (gnd or 0)!
Note the difference in ngspice where the nodes are treated as character strings and not evaluated as numbers, thus 0
and 00
are distinct nodes in ngspice but not in SPICE2.
Ngspice requires that the following topological constraints are satisfied:
- The circuit cannot contain a loop of voltage sources and/or inductors and cannot contain a cut-set of current sources and/or capacitors.
- Each node in the circuit must have a dc path to ground.
- Every node must have at least two connections except for transmission line nodes (to permit unterminated transmission lines) and MOSFET substrate nodes (which have two internal connections anyway).
Basic lines
Title
Example:
POWER AMPLIFIER CIRCUIT * additional comment lines following *...
You can also precede the title with period command .TITLE:
.TITLE line example: * additional lines following * ...
.END Line
The .End
line must always be the last in the input file. Note that the period is an integral part of the name.
Example:
. end
Device Models
General form:
.model mname type ( pname1=pval1 pname2=pval2 ... )
Example:
.model MOD1 npn ( bf=50 is=1e−13 vbf=50 )
Most simple circuit elements typically require only a few parameter values. However, some devices (semiconductor devices in particular) that are included in ngspice require many parameter values. Often, many devices in a circuit are defined by the same set of device model parameters. For these reasons, a set of device model parameters is defined on a separate .model line and assigned a unique model name. The device element lines in ngspice then refer to the model name.
For these more complex device types, each device element line contains the device name, the nodes to which the device is connected, and the device model name. In addition, other optional parameters may be specified for some devices: geometric factors and an initial condition. mname in the above is the model name, and type is one of the following fifteen types:
| Code | Model Type |
|---|---|
| R | Semiconductor resistor model |
| C | Semiconductor capacitor model |
| L | Inductor model |
| SW | Voltage controlled switch |
| CSW | Current controlled switch |
| URC | Uniform distributed RC model |
| LTRA | Lossy transmission line model |
| D | Diode model |
| NPN | NPN BJT model |
| PNP | PNP BJT model |
| NJF | N-channel JFET model |
| PJF | P-channel JFET model |
| NMOS | N-channel MOSFET model |
| PMOS | P-channel MOSFET model |
| NMF | N-channel MESFET model |
| PMF | P-channel MESFET model |
Parameter values are defined by appending the parameter name followed by an equal sign and the parameter value. Model parameters that are not given a value are assigned the default values given below for each model type. Models are listed in the section on each device along with the description of device element lines
Subcircuits
A subcircuit that consists of ngspice elements can be defined and referenced in a fashion similar to device models. Subcircuits are the way ngspice implements hierarchical modeling, but this is not entirely true because each subcircuit instance is flattened during parsing, and thus ngspice is not a hierarchical simulator.
The subcircuit is defined in the input deck by a grouping of element cards delimited by the .subckt and the .ends cards; the program then automatically inserts the defined group of elements wherever the subcircuit is referenced. Instances of subcircuits within a larger circuit are defined through the use of an instance card which begins with the letter X
. A complete example of all three of these cards follows:
* The following is the instance card : * xdiv1 10 7 0 vdivide * The following are the subcircuit definition cards : * .subckt vdivide 1 2 3 r1 1 2 10 K r2 2 3 5 K .ends
The above specifies a subcircuit with ports numbered 1
, 2
and 3
:
- Resistor R1 is connected from port “1” to port “2”, and has value 10 kOhms.
- Resistor R2 is connected from port “2” to port “3”, and has value 5 kOhms.
The instance card, when placed in an ngspice deck, will cause subcircuit port “1” to be equated to circuit node “10”, while port “2” will be equated to node “7” and port “3” will equated to node “0”.
There is no limit on the size or complexity of subcircuits, and subcircuits may contain other subcircuits.
The general form of the .SUBCKT line is:
.SUBCKT subnam N1 <N2 N3 . . . >
Example:
. SUBCKT OPAMP 1 2 3 4
A circuit definition is begun with a .SUBCKT line. SUBNAM is the subcircuit name, and N1, N2, ... are the external nodes, which cannot be zero. The group of element lines which immediately follow the .SUBCKT line define the subcircuit. The last line in a subcircuit definition is the .ENDS line (see below). Control lines may not appear within a subcircuit definition; however, subcircuit definitions may contain anything else, including other subcircuit definitions, device models, and subcircuit calls (see below). Note that any device models or subcircuit definitions included as part of a subcircuit definition are strictly local (i.e., such models and definitions are not known outside the subcircuit definition). Also, any element nodes not included on the .SUBCKT line are strictly local, with the exception of 0 (ground) which is always global. If you use parameters, the .SUBCKT line will be extended.
The .ENDS [subcircuit_name] line must be the last one for any subcircuit definition. The subcircuit name, if included, indicates which subcircuit definition is being terminated; if omitted, all subcircuits being defined are terminated. Therefore, the name is needed only when nested subcircuit definitions are being made.
Last, a subcircuit is called with syntax:
X<any_name> N1 [N2 N3 ...] subnam
For example:
X1 2 4 17 3 1 MULTI
Subcircuits are used in ngspice by specifying pseudo-elements beginning with the letter X
, followed by the circuit nodes to be used in expanding the subcircuit. If you use parameters, the subcircuit call will be modified
Other Dot Directives
.GLOBAL
General form:
.GLOBAL nodename1 [ nodename2 ... ]
Examples:
. GLOBAL gnd vcc
Nodes defined in the .GLOBAL statement are available to all circuit and subcircuit blocks independently from any circuit hierarchy. After parsing the circuit, these nodes are accessible from top level.
.INCLUDE
General form:
.INCLUDE filename
Examples:
. INCLUDE /users/spice/common/bsim3-param.mod
Frequently, portions of circuit descriptions will be reused in several input files, particularly with common models and subcircuits. In any ngspice input file, the .INCLUDE line may be used to copy some other file as if that second file appeared in place of the .INCLUDE line in the original file.
If the filename is a relative path and the file is not found, it is searched for in the locations given by variable sourcepath. There is no restriction on the file name imposed by ngspice beyond those imposed by the local operating system.
.LIB
General form:
.LIB filename libname
Examples:
.LIB /users/spice/common/mosfets.lib mos1
The .LIB statement allows including library descriptions into the input file. Inside the *.lib file a library libname will be selected. The statements of each library inside the *.lib file are enclosed in .LIB libname <...> .ENDL statements. The file is searched for in the same way as for .include.
If the compatibility mode is set to ps
by set ngbehavior=ps in spinit or .spiceinit, then a simplified syntax .LIB filename is available: a warning is issued and filename is simply included.
.PARAM for Parametric netlists
Ngspice allows for the definition of parametric attributes in the netlists. This is an enhancement of the ngspice front-end that adds arithmetic functionality to the circuit description language.
The general form of a .param line is:
.param <ident>= <expr> <ident>=<expr>...
Examples:
.param pippo = 5
.param po = 6 pp = 7.8 pap = { AGAUSS (pippo, 1, 1.67)}
.param pippp = { pippo + pp }
.param p = { pp }
.param pop = 'pp + p'
This line assigns numerical values to identifiers. More than one assignment per line is possible using a separating space. Parameter identifier names must begin with an alphabetic character. The other characters must be either alphabetic, a number, or ! # $ % [ ] _ as special char acters. The variables time, temper, and hertz (see 5.1.1) are not valid identifier names. Other restrictions on naming conventions apply as well.
The .param lines inside subcircuits are copied per call, like any other line. All assignments are executed sequentially through the expanded circuit. Before its first use, a parameter name must have been assigned a value. Expressions defining a parameter should be put within braces {p+p2}, or alternatively within single quotes 'AGAUSS(pippo, 1, 1.67)'. An assignment cannot be self-referential, something like .param pip = 'pip+3' will not work.
The current ngspice version does not always need quotes or braces in expressions, especially when spaces are used sparingly. However, it is recommended to do so, as the following examples demonstrate.
.param a = 123*3 b=sqrt(9) $ doesn't work, a <= 123 .param a = '123 * 3' b = sqrt (9) $ ok . .param c = a + 123 $ won't work .param c = 'a + 123 ' $ ok .param c = a +123 $ ok
Parameters may also have string values, but support is limited. String-valued parameters can be
defined by .param and used in the same ways as numeric parameters. The only operation on
string values is concatenation and that is possible only in top-level .param assignments.
.param str1 = "first" str2 = "second"
.param both ={str1}" and "str2
Brace expressions in circuit elements
General form:
{<expression>}
These are allowed in .model lines and in device lines. A SPICE number is a floating point number with an optional scaling suffix, immediately glued to the numeric tokens. Brace expressions ({..}) cannot be used to parameterize node names or parts of names. All identifiers used within an expr must have known values at the time when the line is evaluated, else an error is flagged.
Subcircuit parameters
General form:
.subckt <identn> node node ... <ident>= <value> <ident>= <value> ...
Example:
.subckt myfilter in out rval =100 k cval =100 nF
<identn> is the name of the subcircuit given by the user. node is an integer number or an identifier, for one of the external nodes. The first <ident>=<value> introduces an optional section of the line. Each <ident> is a formal parameter, and each <value> is either a SPICE number or a brace expression. Inside the .subckt ... .ends context, each formal parameter may be used like any identifier that was defined on a .param control line. The <value> parts are default values of the parameters.
The syntax of a subcircuit call (invocation) is:
X <name> node node ... <identn> <ident>=<value> <ident>= <value> ...
Example:
X1 input output myfilter rval=1k
Here <name> is the symbolic name given to that instance of the subcircuit, <identn> is the name of a subcircuit defined beforehand. node node ... is the list of actual nodes where the subcircuit is connected. <value> is either a SPICE number or a brace expression {<expr>}.
Subcircuit example with parameters:
* Param - example
.param amplitude = 1 V
*
.subckt myfilter in out rval=100 k cval=100 nF
Ra in p1 {2* rval }
Rb p1 out {2* rval }
C1 p1 0 {2* cval }
Ca in p2 { cval }
Cb p2 out { cval }
R1 p2 0 { rval }
.ends myfilter
*
X1 input output myfilter rval=1k cval=1n
V1 input 0 AC {amplitude}
.end
Symbol scope
All subcircuit and model names are considered global and must be unique. The .param symbols that are defined outside of any .subckt ... .ends section are global. Inside such a section, the pertaining params: symbols and any .param assignments are considered local: they mask any global identical names, until the .ends line is encountered. You cannot reassign to a global number inside a .subckt, a local copy is created instead. Scope nesting works up to a level of 10. For example, if the main circuit calls A that has a formal parameter xx, A calls B that has a param. xx, and B calls C that also has a formal param. xx, there will be three versions of xx in the symbol table but only the most local one --belonging to C-- is visible.
.FUNC*
.CSPARAM*
.TEMP*
Circuit Elements*
Models for NGSpice
(Largely from Ngspice Tutorials)
For simulation you need as input to ngspice your circuit (aka the netlist), device models (or model parameters), simulation commands, and output commands. The ngspice distribution does offer some default model parameters only for the basic, intrinsic devices. Device makers, vendors and modeling specialists however provide models, and you may find many of them in the two collections offered below, or in the web (see links below, or search for a specific device or use a general search term like get SPICE models
). Generally PSPICE, HSPICE, and (many) LTSPICE models are compatible with ngspice. Sometimes models by commercial vendors are encrypted. These cannot be used by open source ngspice. Some hints on how to make models and model parameters available to ngspice are given below.
Model and model parameter collections*
PDKs (Process Design Kits) for IC design*
How to add models and model parameters to the ngspice netlist
Basically ngspice may use three types of model data: the PDKs (we will not talk about them here, see our Application page), sets of model parameters for the intrinsic ngspice device models, and complete subcircuit models for OpAmps and other devices and circuits.
A set of model parameters is used by ngspice device models like bipolar or MOS transistors, JFETs or transmission lines. Such a model parameter set is arranged in a single line and looks like
.model mname type ( pname1=pval1 pname2=pval2 ... )
where mname is the model name, type is the ngspice model type, and pnamxx are the model parameters with their values pvalxx. The parameters are prescribed by the model type, the values are used to achieve best correlation between model and measured device reality. Please note that sometimes the .model is split into several lines, where consecutive lines start with a + in the first column. This is regarded as a single line. A typical example is:
*SRC=BC546;BC546;BJTs NPN;Gen. Purpose;65V 100mA .MODEL BC546 NPN (IS=50.7F NF=1 BF=318 VAF=145 IKF=.3 + ISE=15.9P NE=2 BR=4 NR=1 VAR=24 IKR=.45 RE=1.54 RB=6.17 + RC=.617 XTB=1.5 CJE=20.8P CJC=8.33P TF=611P TR=138N) * National 65 Volt .5 Amp 260 MHz SiNPN Transistor 01-26-1991 *PINOUT TO-92 3 2 1
The other model type is a subcircuit model, putting several interconnected intrinsic devices into a subcircuit, which is enclosed in .subckt ... .ends and looks like
.subckt sname node1 node2 node3
...
.ends
Including a Model
.include model-directory/mymodels.lib
or just:
.include mymodels.lib
Now, in a lib file, if you are looking for a model from TI, e.g. the OpAmp OPA1641, you will find a PSPICE compatible, zipped model file assembly sbom627b.zip on the TI web pages. If you unzip it, the ngspice compatible device model is contained in the file OPA1641.lib. To check its eligibility, open the file in a text editor. After the introduction (comment lines), you will find the characteristic line
.subckt OPA1641 IN+ IN- VCC VEE OUT
as the begin of the main subcircuit. So copy this file OPA1641.lib to your model folder and invoke it by:
.include model/OPA1641.lib Xopamp inp inm vc ve opout OPA1641
And because this OpAmp model is a PSPICE model, don't forget to set
set ngbehavior=ltpsa
in file .spiceinit.
*
*
*
Control Language (Formerly Nutmeg)
(From https://ngspice.sourceforge.io/ngspice-control-language-tutorial.html)
ngspice circuit simulator offers a built-in control language (formerly know as nutmeg language). It allows to create scripts which automate the simulation flow and provide support for simulation data analysis.
There are three basic methods to start a simulation with ngspice. The traditional method is the batch mode. With an input netlist given (see below), the command ngspice -b -r inv-example.raw inv-example.cir will start the simulation, executing the dot commands from the netlist, saving the simulation data in the raw
file, without any further user interaction. Besides, we have the interactive mode. You start ngspice by ngspice inv-example.cir. ngspice sources the netlist and then waits for user input. You may type any of the commands found in chapter 17.5 of the ngspice manual or the ngspice xhtml manual to initiate some action (e.g. running the simulation, plotting or saving the data etc.). The real power of using these commands however is unfolded in the third mode of running ngspice, the control mode. You assemble a sequence of commands into a script, add this script to the netlist, and then start ngspice as usual by ngspice inv-example.cir. ngspice will execute the commands from the script, enabling multiple simulations, loops, data processing, plotting and saving data. About 100 commands are available, scripts may range from simple ones with few lines up to complex ones with several hundred commands.
More info on starting ngspice is available in chapter 16.4 of the ngspice manual. Commands used in the scripts below are not all explained in detail, please use the ngspice manual chapter 17.5 as your reference.
.tran and tran*
...
tran 100p 500n
dc and tran*
dc vin 0 2 0.01
Looping a simulation, altering the supply voltage
The next script executes a repeat loop. For five times the inverter supply voltage is changed, each time a new dc simulation is run. Finally all dc simulation results are plotted in a single plot. In addition we plot the inverter gain and its current consumption during the dc sweep.
* control language script .control let vccc = 1.2 ; create a vector vccc and assign value 1.2 repeat 5 ; loop start alter Vcc $&vccc ; alter the voltage Vcc using vector vccc dc vin 0 2 0.01 ; run the dc simulation let vccc = vccc + 0.2 ; calculate new voltage value for Vcc by updating vector vccc end ; loop end, jump back to loop start set xbrushwidth=2 ; assign value 2 to the predefined variable plot dc1.v(out) dc2.v(out) dc3.v(out) dc4.v(out) dc5.v(out) set nounits ; do not plot units on the x and y axes. plot deriv(dc1.v(out)) deriv(dc2.v(out)) deriv(dc3.v(out)) deriv(dc4.v(out)) deriv(dc5.v(out)) ylabel 'Inverter gain V / V' xlabel 'vsweep V' unset nounits ; undo the previous set command plot dc1.I(vcc) dc2.I(vcc) dc3.I(vcc) dc4.I(vcc) dc5.I(vcc) ylabel 'Inverter current consumption' .endc
Several new concepts are introduced in this script. Please refer to chapter 17.5 of the manual for details of the commands. repeat 5 ... end will loop the enclosed commands five times. let vccc = 1.2 creates a vector (of length 1) and assigns a value 1.2 to it. let vccc = vccc + 0.2 adds 0.2 to the current vector value. alter Vcc $&vccc changes the voltage Vcc according to the value of vector vccc. $&vccc returns the value of vector vccc. nounits and xbrushwidth are predefined variables (see manual chapter 17.7). Command plot generates a graphical output by plotting one or several vectors versus a scale vector (predefined is scale vector time
for transient, v-sweep
for dc). The results of the last and all previous dc simulations are accessed by dc1.v(out) dc2.v(out) .... This will be explained in the next paragraph. plot deriv(dc1.v(out)) uses function deriv, returning the derivative of v(out) versus v-sweep (see manual, chapt. 17.2), therefore plotting the voltage gain of the inverter. The supply current during dc sweep is measured as current out of the voltage source Vcc, thus appearing negative.
About plots, variables and vectors
The output data of any simulation is available as vectors. An operating point simulation will create vectors of length 1. A dc simulation will create vectors with length determined by the number of points during sweeping. These vectors are stored in plots, a traditional SPICE notion (see also chapter 17.3 of the manual). Plot
here is not to be confused with a plot resulting from plotting data. So a plot is a group of vectors. In our previous example the first dc command will generate several vectors within a plot dc1. A subsequent dc command will store their vectors in plot dc2 and so on. Transient simulation vectors would have been stored in plots tran1, tran2, etc. So each simulation command (dc, op, tran, sp ...) creates a new plot. The lastly created plot will stay active, until another plot is created or selected. There are also some functions creating their own plots, e.g. fft or linearize. The command setplot will show all plots and mark the active plot. For our example we have this
List of plots available: Current dc5 inverter example circuit for control language tutorial (DC transfer characteristic) dc4 inverter example circuit for control language tutorial (DC transfer characteristic) dc3 inverter example circuit for control language tutorial (DC transfer characteristic) dc2 inverter example circuit for control language tutorial (DC transfer characteristic) dc1 inverter example circuit for control language tutorial (DC transfer characteristic) const Constant values (constants)
with dc5 being the current plot. There is a pre-defined plot named const. It contains several constants. Command display will list all vectors in the current plot. setplot dc2 will switch the current plot to dc2.
Here are the vectors currently active:
Title: inverter example circuit for control language tutorial
Name: dc5 (DC transfer characteristic)
Date: Tue Jun 7 17:25:58 2022
cc : voltage, real, 201 long
in : voltage, real, 201 long
out : voltage, real, 201 long
v-sweep : voltage, real, 201 long [default scale]
vcc#branch : current, real, 201 long
vin#branch : current, real, 201 long
[default scale] denotes the scale vector, which is used as X axis (during plotting or for calculating the derivative). Vectors contain numbers (format double as real or complex numbers). They may contain a single value, be one-dimensional or multi-dimensional. In the above example we have vectors of real numbers of length 201 each. Vectors are local to their plot. You may access vectors from a different plot by prepending the plot name to the vector name, separated by a dot, e.g. dc1.v(out) or dc2.I(vcc). Btw. I(vcc) is equal to vcc#branch, the branch current through voltage source Vcc. The user may create their own vectors with the let command, e.g. by let vccc = 1.2. As this command has been given before any simulation is run, the vector vccc will reside in plot const. All vectors in plot const are globally available, no need to prepend anything.
Another class of storage elements is created by the ngspice variables. These are globally accessible and may contain numbers, strings, or lists. There are pre-defined variables (see manual chapter 17.7) which control many ngspice features. The command set will instantiate a variable and/or assign a content of it. in our example we have used the pre-defined set xbrushwidth=2 to increase the plotting line width.
Looping a simulation, plot the maximum gain versus supply voltage
In the following example the loop is used to simulate and extract the inverter's voltage gain as function of its supply voltage. We simulate the output versus input of the inverter as a series of dc sweeps, the maximum gain for each voltage is determined and saved for plotting.
* control language script .control let loops = 6 ; number of loops, vector in plot 'const' let vecmaxgain = vector(loops) ; vector in plot 'const' let vecvcc = vector(loops) ; vector in plot 'const' let vccc = 1.0 ; supply voltage in plot 'const' let index = 0 ; loop index in plot 'const' repeat $&loops ; loop start alter Vcc $&vccc ; alter the voltage Vcc dc vin 0 2 0.01 ; run the dc simulation let gain = deriv(v(out)) ; calculate the gain let maxgain = vecmin(gain) ; find the maximum of the gain (min because gain is negative) let vecmaxgain[index] = maxgain ; store the maximum gain let vecvcc[index] = vccc ; store the corresponding voltage let vccc = vccc + 0.2 ; calculate new voltage value for Vcc let index = index + 1 ; raise the index end ; loop end destroy all ; delete all plot dc1 ..., no longer needed set xbrushwidth=2 ; linewidth of graph set nolegend ; no legend on graph plot vecmaxgain vs vecvcc xlabel 'Inverter supply voltage /V' ylabel 'Inverter gain V/V' .endc ; end of control section
The vectors loops, vecmaxgain, vecvcc, vccc, and index are created in plot const, which is the current plot in the beginning of the script execution, before any simulation command is run. So these vectors will be accessible from within all plots created later by the simulation runs.
vector loops sets the number of loops and the size of the data storage vectors vecmaxgain and vecvcc. repeat $&loops requires $&loops for reading the vector value.
During each simulation run the voltage Vcc is set, the dc simulation executed, the derivative of the output vector v(out) calculated, its maximum (minimum of the negative gain as function of the sweep voltage) determined and saved to the indexed location of vecmaxgain, together with vccc saved to vecvcc. Finally supply voltage and index are increased appropriately.
After the loop has ended, the command destroy all deletes all plots created during dc simulation (dc1 to dc6). They are no longer needed, so we save their memory. set nolegend supresses the legend, because we have only a single vector plotted. plot vecmaxgain vs vecvcc plots our new output (the max gain) versus the user selected scale, the corresponding supply voltage.
An alternative to the destroy all is to delete only the most recent (current) plot by destroy $curplot, now from inside the loop. curplot is another predefined variable containing a string with the name of the current plot. Accessing the string is possible by the prepended $.
... let index = index + 1 ; raise the index destroy $curplot ; remove the current plot (created by dc...) end ; loop end set xbrushwidth=2 ; linewidth of graph
Looping the simulation as before, no plot, write data to file only
The inverter's voltage gain is determined as in the example above. The result is, however, not plotted, but written into a file as a simple table.
* control language script .control let loops = 11 ; number of loops, vector in plot 'const' let vccc = 1.0 ; supply voltage in plot 'const' repeat $&loops ; loop start alter Vcc $&vccc ; alter the voltage Vcc dc vin 0 2 0.01 ; run the dc simulation let gain = deriv(v(out)) ; calculate the gain let maxgain = vecmin(gain) ; find the maximum of the gain (min because gain is negative) setscale vccc ; define the x axis (scale) with vector of length 1 wrdata mgain.out maxgain ; write out maxgain versus vccc for the current plot let vccc = vccc + 0.1 ; calculate new voltage value for Vcc destroy $curplot ; remove the current plot (created by dc...) set appendwrite ; wrdata now adds data to existing file mgain.out end ; loop end .endc ; end of control section
wrdata writes vector(s) to a file as a simple table. Each vector is written as a pair, first the scale (x axis value), then the output value. In our case the output is maxgain (vector of length 1). The scale after a dc simulation is the voltage sweep. This is not what we need here, as the scale for maxgain is vccc. So we use setscale vccc to re-define the scale (before writing to file). wrdata creates a file or overwrites an existing file with the current data. Here we use a trick: When entering the loop for the first time, wrdata mgain.out maxgain creates the file mgain.out. Then, after first writing, set appendwrite is issued. From then on, wrdata appends all data to the already existing file. Calling set appendwrite several times does not hurt.
About loops in general
Several control structures are available for creating loops. Each loop starts with a keyword and ends with end. Nesting is supported. We use vectors (instatiated by let, de-referenced by $&, like $&loopindex) and variables (instantiated by set, de-referenced by $, like $val).
Here we have foreach...end and if...else...end:
foreach val -40 -20 0 20 40
if $val < 0
echo variable $val is less than 0
else
echo variable $val is greater than or equal to 0
end
end
dowhile allows looping until a condition is fulfilled. The condition is tested after the statements in the loop have been executed:
let loopindex = 0 dowhile loopindex <> 5 echo index is $&loopindex let loopindex = loopindex + 1 end
We have already used the repeat loop:
set loops = 7 repeat $loops echo How many loops? $loops end
while allows looping until a condition is fulfilled. The condition is tested befor the statements are executed:
let loopindex = 0 while loopindex < 5 echo index is $&loopindex let loopindex = loopindex + 1 end
In the Sourceforge git examples for loops page you will find examples netlists for all loops, ready to run. You may also have a look at the manual, chapter 17.6 on Control Structures, where additionally the commands label, goto, break and continue are described.
String substitution
Vectors contain numbers. They may be one-dimensional (often with only a single element, aka length = 1) or multi-dimensional. Variables may contain integer or real numbers, strings, a boolean yes, or a list of elements as strings.
Both are used in the following script as part of new variables. Substitution is done by replacing the {} with the contents of the vector or variable element named inside of the {}.
*ng_script ; this is a script only
.control
* simple replacement
let ii = 1 ; new 1D vector containing value 1
set myplot = tran2 ; new variable containing a string
* substitution
set subs1 = {$myplot}.out ; {} is substituted by string of variable myplot
set subs2 = dc{$&ii}.in ; {} is substituted by vector contents converted to string
echo $subs1 $subs2 ; print both variables
* somewhat more complex substitutions
let vec = vector(6) ; new vector with 6 elements, numbered from 0 to 5
set myplots = ( tran3 dc5 op1 ) ; new variable with list of 3 elements, numbered from 1 to 3
let vec4 = vec[4] ; intermediate stage, read vector element 4 into a new vector.
set subs3 = {$myplots[3]}.out ; {} is substituted by element 3 of string list in variable myplots
set subs4 = dc{$&vec4}.in ; {} is substituted by vector contents converted to string
echo $subs3 $subs4 ; print both variables
.endc
.end
The outputs echoed to the console are:
tran2.out dc1.in op1.out dc4.in
Some comments to this script are due:
*ng_scriptin the upper left of the first row will tellngspicethat a pure script is following, thus skipping any circuit parsing.- Substitution may happen with a single element from a vector or variable.
- Elements of a vector are numbered by 0 to number-of-elements - 1
- elements of a string list in a variable are numbered 1 to number-of-elements.
- Elements of a vector may be used for substituting only after an intermediate step of converting them to a vector of length 1.
- When defining a list variable, take care for spaces between ( or ) and the elements.
Nested loops
In the following netlist there is a very simple circuit: 3 resistors in series, powered by a voltage source. We want to change each resistor using the alter command. We use 3 interlaced while loops.
3 interlaced while loops, simple circuit
R1 1 11 1
R2 11 12 1
R3 12 0 1
V1 1 0 1
.control
* initialization (vectors in plot const)
let rsta1 = 1k
let rsto1 = 5k
let rste1 = 1k
let rsta2 = 200 ; start value for R2
let rsto2 = 1000 ; stop value
let rste2 = 200 ; step value
let rsta3 = 20
let rsto3 = 200
let rste3 = 20
let rloop1 = rsta1
let rloop2 = rsta2 ; loop value for R2
let rloop3 = rsta3
* three nested while loops
while rloop1 < rsto1
alter R1 rloop1
while rloop2 < rsto2
alter R2 rloop2 ; modify R2 by current rloop2 value
while rloop3 <= rsto3
alter R3 rloop3
op
let rtotal = rloop1 + rloop2 + rloop3
echo $&rloop1 $&rloop2 $&rloop3 $&rtotal $&V1#branch
let rloop3 = rloop3 + rste3
end
let rloop3 = rsta3
let rloop2 = rloop2 + rste2 ; calculate new rloop2 value
end
let rloop2 = rsta2 ; reset rloop2 to its start value
let rloop1 = rloop1 + rste1
end
rusage
.endc
.end
In varying each resistor, each resistor gets its vectors for the start, stop and step values. The three rloop vectors will be varied during looping.
One has to be careful where to place the command lines alter R2 rloop2, and let rloop2 = rloop2 + rste2. Right after a loop has finished, its corresponding rloop vector has to be reset to its start value, e.g. by let rloop2 = rsta2 again.
Emulate nested .step commands
Creating nested loops and running several simulations in series without external intervention are a powerful tool to characterize circuits. Unfortunately, currently ngspice does not offer the .step command, which may simplify setting up such simulation loops. However the control language allows us to emulate such behaviour. The netlist below will emulate code like
.step lin R1 list 10k 20k 30k .step lin C2 list 1u 0.5u 0.25u .step lin R2 list 1m 3.3k 6.6k
In the future ngspice may be able to translate such .step commands into emulating control sections automatically and transparently to the user, thus simplifying their job. For now we may use such a netlist for learning about some new features of the control language.
* .step emulation: 3 nested loops, ac simulation
* R1, C2 change by param, R2 changes directly
* loops by foreach
* commands alterparam, alter: all three alter commands inside of inner loop
* alterparam first, followed by reset, only then followed by alter
* print out the results into a file threeloopsac.txt, located in the input
* directory (where this netlist has been found).
.param rr1 = 1k cc2 = 1u
V1 1 0 dc 0 ac 1
R1 1 11 {rr1}
R2 11 10 100
C2 10 0 {cc2}
.ac dec 10 1 1e4
.control
let index = 0 ; new loop index vector (in plot 'const')
set plotstrdb = ' ' ; new variable containing string with space (will become a list of strings, see below)
set plotstrph = ' ' ; new variable containing string with space (will become a list of strings, see below)
set writestr = ' ' ; new variable containing string with space (will become a list of strings, see below)
set cvalues = ( 1u 0.5u 0.25u ) ; variable containing loop values for C2
** loop start **
foreach val1 10k 20k 30k ; outermost loop with a value list
foreach val2 $cvalues ; loop in the middle with a variable containing a value list
foreach val3 1m 3.3k 6.6k ; inner loop with a value list
let index = index + 1 ; raise loop index
echo ac sim no. $&index',' R1=$val1',' R2=$val3',' C2=$val2 ; print to console where we are
alterparam rr1 = $val1 ; change parameter of outer loop
alterparam cc2 = $val2 ; change paramater of middle loop
reset ; reset to activate above parameter changes
alter R2 $val3 ; change inner loop value (possible only after reset!)
run ; run the simulation (see .ac ... above)
set plotstrdb = ( $plotstrdb db({$curplot}.v(10)) ) ; add a new item (string) to already exising list of strings
set plotstrph = ( $plotstrph cph({$curplot}.v(10)) ) ; add a new item (string) to already exising list of strings
set writestr = ( $writestr {$curplot}.v(10) ) ; add a new item (string) to already exising list of strings
end
end
end
** loop end, start plotting **
set nolegend ; legend for 27 graphs is unreadable
set units=degrees ; phase in degrees
set xbrushwidth=2 ; increase linewidth of graphs
plot $plotstrdb xlimit 1 1e4 ; plot all 27 magnitudes
plot $plotstrph xlimit 1 1e4 ; plot all 27 phases
set wr_singlescale ; for wrdata: write the scale only once
set wr_vecnames ; for wrdata: write the vector names
option numdgt = 3 ; for wrdata: 3 digits after decimal point
wrdata $inputdir/threeloopsac.txt $writestr ; write ac output v(10) into file for all runs
rusage ; list some resource usage
.endc
.end
The output file, written by command wrdata, looks like this:
frequency ac1.v(10) ac1.v(10) ac2.v(10) ac2.v(10) ac3.v(10) ac3.v(10) ... 1.000e+00 9.961e-01 -6.258e-02 9.931e-01 -8.299e-02 9.892e-01 -1.032e-01 ... 1.259e+00 9.938e-01 -7.861e-02 9.891e-01 -1.041e-01 9.831e-01 -1.291e-01 ... 1.585e+00 9.902e-01 -9.860e-02 9.828e-01 -1.302e-01 9.734e-01 -1.609e-01 ... 1.995e+00 9.845e-01 -1.234e-01 9.730e-01 -1.622e-01 9.585e-01 -1.995e-01 ... ...
The first column is the frequency, the next 2 columns are the real and the imaginary parts of complex vector v(10) from plot ac1, then follow real and imaginary parts of vector V(10) from plot ac2 etc.
Some comments also here: The circuit is simply two resistors in series (R1 + R2) with capacitor C2 from output v(1) to ground, a low pass filter. We set three variables in the beginning, each containing just a space. '...' delimit a string, even if it contains special characters (e.g. a space). Three nested foreach loops are used with a value list or with a variable containing such a list. The echo command in the loop again uses '...' for formatting. Otherwise spaces or commas would just be swallowed and replaced by a single space. The alterparam command has to be followed by a reset command to become activated. Also, alter commands are resetted by reset. Therefore all three altering commands have to be in the inner loop, repeated each time the loop is executed. Any alterparam has to come first, then reset, only then any alter (or altermod) commands.
set plotstrdb = ( $plotstrdb db({$curplot}.v(10)) ) requires some explanation: {$curplot}.v(10) substitutes the {} part with the name of the current plot (e.g. ac3), just to obtain, say, ac3.v(10). This string, embedded in db(...) is added to the variable plotstrdb, increasing its string list. In the beginning plotstrdb started with a space, then one item is added per loop, e.g. yielding db(ac1.v(10)) db(ac2.v(10)) db(ac3.v(10)) ....
Another group of .step commands loops the temperature and device parameters given by a start, stop and step value for each parameter, like
* .step temp -55 125 10 * .step npn 2N2222 (VAF) 50 300 50 * .step param R1 2k 10k 2k
Again there are three nested loops, but now we use the while ... end loop command.
*** Emulate the .step command
.param ptemp = -55 R1 = 2k
.temp {ptemp} ; set the overall circuit temperature
V1 vcc 0 5 ; the circuit
R1 vcc cc {R1}
Q1 cc bb 0 BC546B
R2 vcc bb 500k
.probe I(Q1) ; measure the Q1 terminal currents
.save all ; not only save the Q1 current values, but all node values
.op
.control
let index = 1 ; new loop index vector (in plot 'const')
let tcur = 25 ; new temperature vector (in plot 'const')
while tcur <= 125 ; the temperature loop
let mvaf = 50 ; new model parameter vector (in plot 'const')
while mvaf <= 300 ; the VAF loop
let rr1 = 2k ; new resistance parameter vector (in plot 'const')
while rr1 <= 10k ; the resistor R1 loop
echo
echo
echo *** op no. $&index',' R1=$&rr1',' VAF=$&mvaf',' temp=$&tcur *** ; print to console where we are
alterparam ptemp = $&tcur ; change the temperature parameter
alterparam R1 = $&rr1 ; change the R1 resistance parameter
reset ; activate the parameter changes by reloading the ciruit
altermod BC546B VAF = $&mvaf ; change the VAF model parameter
run ; run the op simulation
print v(cc) v(bb) i(q1:c) i(q1:b) ; the data output, i(q1:c) is the same as q1:c#branch
let rr1 = rr1 + 2k ; new resistance value
let index = index + 1
end
let mvaf = mvaf + 50 ; new VAF value
end
let tcur = tcur + 10 ; new temperature value
end
display
rusage
.endc
*From Philips SC04 "Small signal transistors 1991"
* Base spreading parameters (RB,IRB,RBM) estimated. TR derived using BCY58 data
.model BC546B npn ( IS=7.59E-15 VAF=73.4 BF=480 IKF=0.0962 NE=1.2665
+ ISE=3.278E-15 IKR=0.03 ISC=2.00E-13 NC=1.2 NR=1 BR=5 RC=0.25 CJC=6.33E-12
+ FC=0.5 MJC=0.33 VJC=0.65 CJE=1.25E-11 MJE=0.55 VJE=0.65 TF=4.26E-10
+ ITF=0.6 VTF=3 XTF=20 RB=100 IRB=0.0001 RBM=10 RE=0.5 TR=1.50E-07)
.end
So what is new here? Command .temp {ptemp} sets the overall circuit temperature to the value of parameter ptemp. We now use an active device, the npn bipolar transistor Q1, whose model parameters are given in the .model BC546B npn ... line. The .probe(Q1) command allows to measure the device Q1 terminal currents as shown by the display command:
q1:b#branch : current, real, 1 long
q1:c#branch : current, real, 1 long
q1:e#branch : current, real, 1 long
The three nested loops start with while tcur <= 125. One has to be careful where to place correctly the initial loop value let tcur = 25 and the command let tcur = tcur + 10 to increase the value. Changing the model parameter VAF (forward Early voltage) is done by altermod, again after setting the parameters by alterparam and after reset. Data output is provided here by the simple print command.
The above netlist implicitely provides a linear increase of parameters. A .step command like
* .step oct v1 1 100 5
with its option oct implies a non-linear distribution of input parameters (here 5 values per octave, that is within a factor of 2). A netlist with a single loop demonstrates this command:
*** Emulate the .step command
* .step oct v1 1 100 5
V1 vcc 0 1 ; the circuit
R1 vcc 0 1
.op ; simulation type required
.control
let vstart = 1 ; start voltage vector (in plot 'const')
let vend = 100 ; end voltage vector (in plot 'const')
let numb = 5 ; number of points per octave (in plot 'const')
let index = 1 ; new loop index vector (in plot 'const')
let vmult = 2^(1/numb) ; multiplicator
let xx = vend/vstart ; find the total number of steps
let ind1 = 0
while xx > 1
let xx = xx / vmult
let ind1 = ind1 + 1
end
echo number of steps $&ind1
if ind1 < 1 ; move on when number of steps is positive
echo error with number of steps $&ind1 --'>' stop!
else
let vecx = vector(ind1) ; create vectors for x and y
let vecy = vector(ind1)
settype voltage vecx ; set the correct vector type
settype current vecy
let vcur = vstart ; current voltage value in the loop
while vcur <= vend ; the voltage loop
alter V1 vcur ; a new voltage value for V1
echo run no. $&index
run ; simulate
let indloc = index - 1 ; indexes for vectors start with 0
let vecx[indloc] = vcur ; x value into vector
let vecy[indloc] = v1#branch ; y value into vector
* print vcur v1#branch
unlet indloc ; remove vector no longer used
echo
let vcur = vcur * vmult ; calculate new voltage value
let index = index + 1
end
plot vecy vs vecx pointplot xlog ; plot y versus x
end
rusage ; memory and time used
.endc
.end
Here we use a multiplicator to alter the voltage value, derived from the information "5 values per octave". After calculating the total number of steps, we create and use 2 vectors vecx, vecy to store the result of the op simulation versus the V1 value. Some checking against wrong point numbers is done. A pointplot in Fig. 5 demonstrates the uniform x value distribution, when plotted logarithmically.
Modify a circuit on the fly
By using the .if ... .elseif ... .endif construct, you may choose from different circuit blocks which are enclosed within the conditional statements. Selection is done by altering a parameter as selector with the alterparam command.
In the first example we switch between a simple low pass and a high pass circuit, run an .ac simulation for each one and plot the output in a single plot window.
High pass, low pass on the fly
.param select = 0 ; selector
.IF (select == 0) ; select low pass
R1 in out 1k
C1 out 0 1u
.ELSEIF (select == 1) ; select high pass
R1 out 0 1k
C1 in out 1u
.ENDIF
Vin in 0 dc 0 ac 1 ; input voltage
.ac dec 10 1 100k
.control
run ; run the simulation
alterparam select = 1 ; modify the selector from 0 to 1
reset ; reload the circuit with new selector, so with new circuit
run ; run the simulation again
set xbrushwidth=3 ; increase linewidth of graphs
plot db(ac1.out) db(ac2.out) xlimit 1 1e5 ; plot both simulations in one graph
.endc
.end
Some restrictions apply, as the following netlist components are not supported within the .IF ... .ENDIF block: .SUBCKT, .INC, .LIB, and .PARAM.
In the next example we choose from 2 different operational amplifiers. We have to include all of their models, and we select by calling the appropriate model in the call to their subcircuit (X line).
* Modify circuit (here: changing OpAmps) using .IF ... .ENDIF
.param select = 0 ; selector
.IF (select == 0) ; select OpAmp
X1 0 INM P6V M6V OUT OPA1656
.ELSEIF (select == 1)
X2 0 INM P6V M6V OUT OPA164x
.ENDIF
R1 INM OUT 10K ; inverting amplifier, gain -2
R2 IN INM 5k
V1 P6V 0 4V ; power supply
V2 M6V 0 -4V
Vin IN 0 dc 0 ac 1 ; input (for ac)
.ac dec 20 1e4 1e8 ; ac simulation conditions
.include OPA1656.lib ; download OpAmp model from TI's web site
.include OPA164x.lib ; download OpAmp model from TI's web site
.control
save out ; save only the output, all other nodes are discarded
run ; run the simulation
alterparam select = 1 ; modify the selector from 0 to 1
reset ; reload the circuit with new selector, so with new OpAmp
run ; run the simulation again
let dbout1 = db(ac1.out) ; calculate db in preparation for meas command, previous run
let dbout2 = db(out) ; calculate db in preparation for meas command, current run
set xbrushwidth=3 ; increase linewidth of graphs
plot dbout1 dbout2
meas ac fmin3db1 when dbout1=3 ; gain 6 dB, measure fall-off to 3 dB
meas ac fmin3db2 when dbout2=3 ; gain 6 dB, measure fall-off to 3 dB
.endc
.end
Finally we use two meas(ure) commands to find the frequency where the opamp gain has rolled off by -3 dB.
Comments
The general form of a whole line comment is:
Examples:
The asterisk in the first column indicates that this line is a comment line. Comment lines may be placed anywhere in the circuit description.
The general form of end-of-line comments is:
Examples:
ngspice also supports comments that begin with single characters ’;’ or double characters ’$ ’ or ’//’ or ’- -’.