Skip navigation
All Places > Keysight Blogs > EEsof EDA > Blog
1 2 3 Previous Next


126 posts

Want to find out how to design a smaller, lighter, and lower cost switched-mode power supply? Please register for our Keysight Engineering Education (KEE) Webinar on this topic, that we will present on Wednesday January 30th 2019, 10AM Pacific/1PM Eastern.


Bring your questions to our live Q&A session!


Registration link:


Engineers building switched-mode power supplies into their systems are demanding lower cost, smaller size, and lighter weight. In general, faster switching speed – high di/dt in the industry jargon -- enables smaller, lighter, and cheaper versions to be made. Learn how to identify and control the post-layout parasitic effects that degrade switched-mode power supply performance to design a more efficient switched-mode power supply.   

Three Key Learnings: 

  • Understand the limitations to traditional switched-mode power supply design. 
  • Learn to identify spike voltages induced across layout parasitics. 
  • Know how and why to do post-layout analysis before sending the layout to fabrication.

Hope to see you there!

When you create a Verilog-A version of a VHDL analog model, you'll want to verify that the two match. A convenient way to do this is to compare things like I-V curves, for example. The ADS simulator can place data for the I-V curve of your Verilog-A model directly into the dataset used by ADS Data Display. But how do you get the VHDL version in? First you'll have to export the data from your VHDL simulator. The most convenient format for ADS to read is a tab separated plain text file. However, for ADS to make sense of the data, it also needs information in the form of a header and a footer, so you might need to open up the raw data in a plain text editor and type them those in manually. There are several styles and formats possible, but let's pick a simple one but very flexible one. (The technical name is "Generic MDIF" if you want read the full specification in the doc.)


See the sample below. Lines that begin with ! are comments. The beginning of an array of data is marked with BEGIN nameofmydata. The next line, beginning with a %, specifies a column name and its data type e.g. Vd(real). You need one specification per column of data.

Then you have the tab separated data itself, two columns in my case. Finally, you need an END line. Here is a complete example:

!Optional comment like "this data was generated by running a VHDL model"
% Vd(real) Id(real)
-1.000E+00 -1.000E-13
-9.000E-01 -1.000E-13
-8.000E-01 -1.000E-13
-7.000E-01 -1.000E-13
-6.000E-01 -1.000E-13
-5.000E-01 -1.000E-13
-4.000E-01 -1.000E-13
-3.000E-01 -1.000E-13
-2.000E-01 -9.987E-14
-1.000E-01 -9.643E-14
0.000E+00 0.000E+00
1.000E-01 2.703E-12
2.000E-01 7.848E-11
3.000E-01 2.203E-09
4.000E-01 6.174E-08
5.000E-01 1.731E-06
6.000E-01 4.852E-05
7.000E-01 1.360E-03
8.000E-01 3.812E-02
9.000E-01 1.069E+00
1.000E+00 2.996E+01


To import its data via the Data File Tool, it is convenient to place the plain text file in the data folder of your workspace. The file name extension should be *.mdf . For example, if your workspace is C:\Users\cwarwick\Workspaces\shared_wrk, its data folder is C:\Users\cwarwick\Workspaces\shared_wrk\data and so the full path of your data file should be something like C:\Users\cwarwick\Workspaces\shared_wrk\data\myvhdldata.mdf 

  1. From the Data Display menu bar, select Tools-->Data File Tool.
  2. From the Data File Tool dialog box, set the "Mode" radio button to "Read data file into dataset."
  3. Click on the "Browse...' and select your data file.
  4. From the "File format to read" list, select "MDIF"
  5. From the MDIF sub type list, select the last option "Generic MDIF"
  6. In the "Dataset name" text box type a name such as "VHDLcompare"
  7. Click on the "Read File' button.
  8. From the Data Display menu bar, select Insert-->Plot...
  9. Click anywhere on the drawing canvas.
  10. In the "Plot Trace & Attributes" dialog box, select VHDLcompare (or what ever name you gave it) from the drop down list.
  11. For an I-V type plot, first select the name of the current vector (Id in our example), then click on the ">>Add Vs..>>" button, then select the corresponding voltage vector (Vd in our case). Click on OK, to dismiss, and OK again to dismiss the dialog box and see your plot.



You can overlay data from other datasets such datasets generated by simulations including your Verilog-A model and check that the models match.

In Part 1 we only translated a simple resistor. Let's look at a more complicated model, a diode with a junction capacitance that varies with voltage. Here is the model in VHDL-A, keywords in bold:

library IEEE, Disciplines;
use Disciplines.electrical_system.all;
use IEEE.math_real.all;
entity diode_cap is
   generic (

      i0: REAL := 0.0; -- amps

      tau: REAL := 0.0; -- seconds

      c0: REAL := 0.0; -- farads

      vj: REAL := 0.0); -- volts
   port (terminal a, k: electrical);
end entity diode_cap;
architecture simple of diode_cap is
   quantity vdiode across idiode, icap through a to k;
   quantity qcap: charge;

      constant vt: REAL := 0.0258; -- thermal voltage at Tj = 300K in volts
   idiode == i0 * (exp(vdiode / vt) - 1.0);
   qcap == tau * idiode - 2.0 * c0 * sqrt(vj**2 - vj * vdiode);
   icap == qcap’dot;
end architecture simple;


It is similar in structure to the resistor example in part 1, but there are three new ideas added:


First, the implicit parallel connection of the two current branches idiode and icap that you can see in the line:

   quantity vdiode across idiode, icap through a to k;

Second, the usage of the "tick dot" notation to apply the time derivative method onto qcap in the line:

   icap == qcap’dot;

Third, charge is a data type in VHDL-A's electrical nature.


My translation to Verilog-A is:

`include "disciplines.vams"

module diode_cap(a, k);
   parameter real i0=0.0; // amps
   parameter real tau=0.0; // seconds
   parameter real c0=0.0; // farads
   parameter real vj=0.0; // volts
   real qcap;
   inout a, k;
   electrical a, k;
   branch (a, k) diode, cap;
   analog begin
      I(diode) <+ i0 * (limexp(V(a,k) / $vt(300)) - 1.0);
      qcap = tau * I(diode) - 2.0 * c0 * sqrt(vj**2 - vj * V(a,k));
      I(cap) <+ ddt(qcap);

Notable differences compared to part 1 and to the VHDL version are:


First, that branch is an explicit keyword in Verilog-A. Here we declare two named branches diode and cap both of which are between nets a and k, which renders them in a parallel (aka shunt) configuration. You can apply the access functions V() and I() to named branches so V(a,k) is the same as V(cap) and V(diode) in our case. (By the way, to connect branches in series instead of parallel, you would declare an internal node, say electrical i; , and write something like branch (a, i) rs, (i, k) diode; )


Second, that the time derivative is a function ddt() not a method. If the capacitance had been a constant c, you could have used I(cap) <+ c * ddt(V(cap));


Third, I used a real to represent charge. There is a Charge nature in the standard include file disciplines.vams Verilog-A but it's not useful in this context. The variable qcap is internal to the model, so we omit the parameter keyword, because we don't want to expose it in the model's parameter list. Because qcap is an ordinary variable, we cannot use the <+ contribution operator: we must use the assignment operator = .  For this reason, the order of the statements is important. The qcap assignment must be placed before the contribution statement that uses the result. This is a subtle but important difference between Verilog and VHDL and it is worth your time to ponder it. For a discussion, see page 58 of "The Designer's Guide to Verilog-AMS" by Kundert and Zinke.


Note that I chose to highlight one of the built-in system functions $vt() for the thermal voltage rather than defining a constant.  You can use an explicit temperature like $vt(300) or the simulator temperature by writing $vt($temperature). If you omit the parentheses and argument, simply $vt, it is equivalent to $vt($temperature). By the way, the temperature is in kelvin, so be sure not to accidentally set it to zero! Zero kelvin is non-physical obviously, and besides that it can trigger horrible math library exceptions like divide by zero or zero to the power zero.


Note also that I chose to use limexp() instead of exp(). limexp is short for exponential with limiting. It limits how fast the return value can change from call to call. It often improves simulator convergence versus its cousin, the more usual exp().


In Part 5, I turn to a slightly different topic, namely verification.

IC-CAP comes with a powerful library of transforms and examples to help with model parameter extraction but when implementing custom analysis routines, it is sometimes necessary to use Python with external Python libraries like Numpy or SciPy for manipulating your measured data. Numpy, in particular, has a rich set of numerical processing features.  Why not leverage these functions and libraries?

Sounds good, but you should not blindly use 3rd party libraries without understanding the performance or accuracy of the functions and whether or not they meet your requirements. An example is computing the first order derivative of an I-V curve, a common task in device modeling. There are multiple methods for computing the derivative using finite differences, which can produce different results due to round-off errors or other numerical artifacts. I'm going to demonstrate several methods for computing a derivative and compare the results to show you how to evaluate and implement the best numerical method for your application.

What is a Derivative?

Definition 1:
The mathematical definition of the derivative of a function f(x) at point x is to take a limit as "h" goes to zero of the following expression:

df/dx ~ ( f(x+h) - f(x) ) / h

Approximations of this form are called "finite differences", of which there are many variations.

Definition 2:
A better way of numerically computing the derivative for the same value of "h" is by taking a symmetrical interval around x as follows:

df/dx ~ ( f(x + h/2) - f(x - h/2) ) / h

This particular difference formula is often called a "second-order centered difference" method, while Definition 1 is known as a "first-order forward difference" method. Since these numerical methods are really just approximations, the "second-order" and "first-order" refer to the accuracy.  Second-order means that the error in the approximation decreases with decreasing h proportional to h*h, while first-order decreases only proportional to h.
If we think about "h" as a Dx,  we can see from the figure above that the symmetrical central difference method provides a better estimate of the derivative at point x.
Finite difference method for computing derivatives
(Image courtesy of Wikimedia.  By Kakitc - Own work, CC BY-SA 4.0)



The python_derivative_v1_1.mdl IC-CAP model file attached at the bottom of this post includes several transforms to compute the derivative:


  • Using Python Numpy functions - gradient() and diff()

  • Using the PEL (Programming Extraction Language) derivative function explored in a transform named Test_PEL



I will illustrate the use of IC-CAP's built-in derivative functions in PEL as follows.

#  Test_PEL 
#  Computes the derivative of I-V data using PEL derivative2 function
complex x[34] x[0]=0 x[1]=0.05 x[2]=0.1 x[3]=0.15 x[4]=0.2...x[33]=1.65
complex y[34] y[0]=8.33E-12 y[1]=3.607E-11 y[2]=1.577E-10 y[3]=6.9396E-10 y[4]=3.0463E-9...y[33]=1.7874E-5

der = derivative2(x,y,1,34)

print der

complex gm.M.11[size(der)]

while i < 34
  gm.M.11[i] = der[i]
  i = i + 1
end while
return gm


I wanted to implement the same derivative calculation using the IC-CAP Python environment. I've developed a new library for implementing some numerical methods including two different derivative functions that can be called from our transforms. This file is included as an attachment to this article. Download the file and copy it to your ICCAP_USER_PYTHON_PATH (typically "C:\Users\<username>\iccap\python").


In my first attempt, I used the diff function from the Numpy library as shown in the function icm_derivative_npdiff code listing from


import numpy as np

def icm_derivative_npdiff(x,y):
   # create numpy array and initialize
   dydx = np.zeros([len(x),])
   # create numpy arrays
   dx = np.zeros([len(x),1])
   dy = np.zeros([len(y),1])

   # flatten and copy arrays
   dx.flat[:] = x
   dy.flat[:] = y
   # compute diff function for x and y values
   # uses first order forward differencing method
   ddx = np.diff(x)
   ddy = np.diff(y)
   # compute the derivative
   dydx = ddy/ddx

   return dydx


Now that we have the function defined in our library we can import it from our Test_npdiff transform and call the function to compute the derivative of our sample I-V data set.




#  Computes the derivative of I-V data using Numpy Diff function
from iccap import set_return_array
import numpy as np
import ic_math as m

# Using I-V data Id vs. Vg
x = np.array([0, 0.025, 0.05, 0.075, 0.1, 0.125, 0.15, 0.175, 0.2, 0.225, 0.25, 0.275, 0.3, 0.325, 0.35, 0.375, 0.4, 0.425, 0.45, 0.475, 0.5])


y = np.array([1E-14, 5.1E-14, 1.23E-13, 2.52E-13, 4.58E-13, 8.73E-13, 1.612E-12, 2.932E-12, 5.418E-12, 1.0036E-11, 1.8614E-11, 3.4948E-11, 6.5647E-11, 1.23711E-10, 2.33938E-10, 4.42538E-10, 8.39282E-10, 1.59355E-9, 3.04727E-9, 5.797E-9])


# forward difference approximation

dydx = m.icm_derivative_npdiff(x,y)

if debug: print dydx


gm = [ dydx[i] for i in range(0,len(x)-1) ]

if debug: print "initial gm = {}".format(gm)


# insert dydx starting element to gm at index = 0

# dydx array has 1 less value due to diff function return


if debug: print "gm = {}".format(gm)


set_return_array("M", gm)


In my experience, this is usually adequate and the error is negligible when performing extractions of threshold voltage Vth from the Id vs. Vg characteristics of a MOSFET transistor.  During this calculation, we compute the derivative of Id with respect to Vg and then find the maximum of the derivative (i.e. Gm_max).  At that interpolated Vg, we define a linear function whose slope is equal to Gm_max to superimpose on the IdVg curves. This is illustrated in Ma Long's blog post entitled "Device Modeling 101 - How to Extract Threshold Voltage of MOSFETs." The error in performing these steps is usually greater than any round-off error that may have occurred in computing the derivative. In other words, the simple forward difference approximation for the derivative is usually close enough for this application. 


After running the Test_npdiff transform and comparing the results to the built-in IC-CAP PEL function, I noticed that there was a discrepancy in the computed derivatives, especially in the first points of the I-V curve. The numpy.diff() function was slightly under-estimating the derivative even-though the overall slope of the line was pretty close. The maximum error was at the lower boundary of the curve. Furthermore, the vector returned was one point less in length.  When plotting the derivative result against the original I-V data, I had to pad the first point to realign the data points.




Was there an error in my math?  Looking at the PEL implementation of the derivative, I noticed that it was using a central difference approximation method which is often more accurate. The simple algorithm shown above using numpy.diff() uses the forward difference approximation method, which can be subject to round-off errors when the step size is small. 


After some more investigation into the Numpy library functions, I discovered a function that will produce the same result of the Test_PEL transform - the numpy.gradient() function. This function uses the second order accurate central difference method for most of the vector. Furthermore, the vector length is preserved, by gracefully handling the lower and upper boundaries. To evaluate this method, I created an additional Python function in the ic_math library called icm_derivative_npgrad to implement the gradient method as shown below in the code listing:


def icm_derivative_npgrad( x, y, e1, e2 ):
   # create numpy array and initialize
   dydx = np.zeros([len(x),])
   # create numpy arrays
   dx = np.zeros([len(x),1])
   dy = np.zeros([len(y),1])

   # flatten and copy arrays
   dx.flat[:] = x
   dy.flat[:] = y

   # compute gradient function for x and y values
   # uses second order central differencing method
   ddx = np.gradient(x, edge_order=e1)
   ddy = np.gradient(y, edge_order=e2)

   # compute the derivative
   dydx = ddy/ddx
   return dydx



Now we can call the new icm_derivative_npgrad function from our transform named Test_npgrad which will uses the gradient function.


#  Computes the derivative of I-V data using Numpy Gradient function
from iccap import set_return_array
import numpy as np
import ic_math as m


# using I-V data Id vs. Vg
x = np.array([0, 0.025, 0.05, 0.075, 0.1, 0.125, 0.15, 0.175, 0.2, 0.225, 0.25, 0.275, 0.3, 0.325, 0.35, 0.375, 0.4, 0.425, 0.45, 0.475, 0.5])


y = np.array([1E-14, 5.1E-14, 1.23E-13, 2.52E-13, 4.58E-13, 8.73E-13, 1.612E-12, 2.932E-12, 5.418E-12, 1.0036E-11, 1.8614E-11, 3.4948E-11, 6.5647E-11, 1.23711E-10, 2.33938E-10, 4.42538E-10, 8.39282E-10, 1.59355E-9, 3.04727E-9, 5.797E-9])


# forward difference approximation

dydx = m.icm_derivative_npgrad(x,y,1,2)

if debug: print dydx


gm = [ dydx[i] for i in range(0,len(x)-1) ]

if debug: print "initial gm = {}".format(gm)


set_return_array("M", gm)


You may notice that I've used a new function set_return_array() added to the IC-CAP 2018 Python API which returns a list from a temporary variable to the Transform dataset. This allows this data to be accessible for plotting and is analogous to the RETURN statement in PEL. 


As you can see from the table below, the icm_derivative_npgrad function yields exactly the same results as the PEL derivative2 function.  Meanwhile, the numpy.diff function underestimates the derivative.  I padded the numpy.diff() generated gm data so the first point is non-zero and of the same magnitude of the other data to allow easily plotting the results above.  See that the blue and green traces in the graph above are right on top of each other.




iccap derivative comparison table



There have been some requests on the SciPy forums to improve the derivative function in the SciPy math libraries to include more accurate methods for calculating the derivative.  But it is up to the open-source community to prioritize the development and release this feature. For now you can still use functions like numpy.gradient() to perform the same derivative computation as the built-in libraries in IC-CAP.  This kind of trade-off between accuracy is quite common with numerical methods for computing derivatives. There are some other 3rd Party libraries that support other methods for numerical differentiation but often require more compute time.  

In my previous post, I showed you the "all-in-one" method for adding your Verilog-A models to ADS. In this post, I'll show you another method called "shared library." It's a bit more work to set up, but it saves time in the long run because it avoids duplication of effort in each new project. If you give a project workspace to a colleague you have to remember to give the referenced library also. In fact, this shared library method is just good not only for Verilog-A components but also for component models of other types (e.g. netlist-based models/model cards). It's a good method anytime you have component models that are common to several projects. Like last time, there are quite a few steps, but each one is really easy.

Shared Library

  1. Follow steps 1 through 20 of Part 2, except this time, in step 2, name the workspace something like shared_wrk. Alternatively, make a copy of all_in1_wrk, rename it shared_wrk, and then delete the testbench cell.
  2. In the Main window, select the Library View tab. When you created the workspace, ADS automatically created a default library under it. The default name is created by replacing the _wrk suffix with a _lib suffix. So in our case it is called shared_lib. Right click on the library and select Configure Library... from the popup context menu.
  3. In the Library Configuration Editor, Simulation tab, click on the Browse... button to the right of the Verilog-A directory edit box. Browse up one level and select the veriloga folder. Alternatively, you can just type ..\veriloga into the edit box. The relative path-name is preferred over an absolute path because if you give the library to a colleague, they might install it with different root name. Dismiss the Library Configuration Editor by clicking on the OK button. It reminds you that changes will take effect next time you open it. Click OK.
  4. From the main window menu bar, select File-->Close Workspace. We are done with the shared library for now. Let's imagine we are starting the first of several projects that will refer to the shared library. 
  5. From the ADS Main Window menu bar, select File-->New-->Workspace...
  6. From the New Workspace dialog box, give it a name like project1_wrk and then click the Create Workspace button.
  7. From the Main window menu bar, select File-->Manage Libraries...
  8. From the Manage Libraries dialog box, click on the "Add Library Definition File..." button.
  9. From the Select Library Definition File browser, navigate to the previous workspace (i.e. your shared_wrk folder) and locate and select a file called lib.defs then click the Open button.
  10. Dismiss the Manage Libraries dialog box by clicking on the Close button.
  11. In the Main window Folder View tab, click on the little arrow by the resistor cell folder. You should see the symbol view of the cell.
  12. From the Main window menu bar, select File-->New-->Schematic... and name its cell something like testbench.
  13. Create Arrange the Main window and the new testbench schematic window side-by-side.
  14. Click and drag the resistor symbol from the Main window to the testbench schematic window. Instantiate your resistor by clicking anywhere on the schematic canvas. Set it to 2 ohms.
  15. Create a simple testbench like this:
  16. Click on the Simulate icon or select Simulate-->Simulate or just hit the F7 shortcut key. The first time you simulate, there is a short pause while ADS compiles the Verilog code. It saves the compiled model, so if you don't touch the code, there is no need to re-compile the next time you run it.
  17. From the Data Display window, insert a plot, and select the Vout and branch current (_ub_p_n_i) traces:
    voltage and current through 2 ohm resistor
    I added some markers and played around with the axis and trace settings.

Success! Now you create project_2, project_3 and so on. Repeat steps 5-17 for each new project to refer to your shared library thus reusing it.


In the next posting, we'll return to the main topic: how to translate the analog parts of VHDL-AMS to Verilog-A. Part 1 was a resistor. Part 4 is a comparison of how the two languages handle time derivatives and internal branches.

In my previous post, showed you how to create the Verilog-A code for a component model. In this post I'll show you how to import the code into ADS. If you don't have access to ADS or you're not familiar with it, I suggest you read our Quick Start Guide first.


There are two strategies for adding a model to an ADS workspace, let's call them "all-in-one" and "shared library" methods. "All-in-one" is the simplest and as the same suggests it is self-contained. "Shared library" is a bit more work to set up, but it saves time in the long run because you can reference the same library from each new project, and avoid having to recreate it every time.

I cover the all-in-one method in this post and the shared library in the next.

(Side note: There is also a further Verilog-A flow in the W2319 ADS RFIC Interoperability Element, but I won't be covering it in this series. If you are interested, the doc page is here.) 

All-in-one method

  1. Launch ADS. From the ADS Main Window menu bar, select File-->New-->Workspace
  2. In the "New Workspace" dialog box, give your new workspace a name such as all_in1_wrk 
  3. Click on the "Create Workspace" button.
  4. In the Main window "Folder view" tab, you will see a workspace folder whose name ends in ...\all_in1_wrk. Right click on it and select the last option in the pop up context menu, namely "Explore In File System".
  5. Assuming Windows OS, File Explorer opens in the workspace's folder on your file system. Right click on a blank space in the right panel and select New-->Folder. It is best to name the folder veriloga because then it will be on the default search path that the Verilog-A compiler looks in.
  6. Open your new folder and, using copy-paste and your favorite plain text editor, create the file containing the Verilog-A source code from Part 1. Name the file (The first part can be anything convenient, but the file extension must be either *.va or *.vams for the compiler to recognize it.) You can close your text editor and Windows Explorer if you like.  
  7. Go back to the ADS Main window and from its menu bar, select File-->New-->Symbol view...
  8. In the "New Symbol" dialog box, overwrite the default cell name (cell_1) with  resistor. Note that you are creating not only a view but also a cell. A cell is a container that holds one or more views of a component or a circuit. You never view or edit the cell directly but instead you view and edit it via one of its views. It is important that the cell name, resistor, matches the module name, resistor, in the Verilog code.
  9. In this "New Symbol" dialog box, click the "Create Symbol" button. Two new windows open: The Symbol Generator dialog box and the Symbol canvas itself.
  10. In the "Symbol Generator" dialog box, make sure you are on the Copy/Modify tab, then select "Lumped-Components" from the "Symbol category" drop down list. Click on the resistor icon. The "Symbol name" edit box will populate as ads_rflib:R . Click OK to dismiss the "Symbol Generator" dialog box. By the way, you can draw your own symbol if there is no suitable one already: Search for the "Draw a Custom Symbol" topic in the doc.
  11. In the Symbol drawing canvas you can see the symbol as been automatically created, with pins 1 and 2 with default names P1 and P2. Double click on each pin in turn and change the name to match those listed in the Verilog code module resistor(p, n); i.e. set the name of pin 1 and 2, to p and n, respectively.
  12. From the Symbol window menu bar select File-->Design Parameters.
  13. In the "Design Parameters" dialog box's "General Cell Definition" tab, set the "Component Instance Name" to whatever prefix you want instances to have, R for example. Instances will be given default names R1, R2, R3, etc.
  14. Important! Uncheck the "Subcircuit" check box. (If you are curious, the difference between a subcircuit and a leaf node is that you can push into the hierarchy of an instance of a subcircuit, but not into a leaf node. Try it! Right click on any instance of a component in a schematic and select "Push Into Hierarchy" from its pop up context menu. If the instance is a subcircuit, the subcircuit will open. If not, you get a "Cannot push into this instance" error. This check box tells the netlister which type (subcircuit or leaf node) it is dealing with, so it can traverse the hierarchy correctly.)
  15. In the Simulation frame, select "Subnetwork" from the Model drop down list. (Wait a minute Colin, I thought you told me in the last step that what we are building is not a subcircuit? Well, it is isn't. It isn't a subcircuit, but it is a subnetwork. Yikes! A subnetwork is in contrast to a built-in component. The third option for "Model" is non-simulatable items like text annotations.) 
  16. Select the "Cell Parameters" tab of the "Design Parameters" dialog box. Remember the line parameter real r=0.0; in the Verilog code? Here is where you connect to it. In the Edit Parameter: Parameter Name edit box type r, leave the Value Type as "Real", set Parameter Type/Unit to Resistance, and type something helpful like "Resistance in ohms" for the parameter description. It is best to leave the Default Value blank. You can put a value in there, but it will override the default value in the Verilog-A file, which might be confusing if the two values get out of sync at some point. 
  17. Look to the left side of the same tab, and click on the "Add" button. The r parameter will be added to the parameter list box.
  18. Click OK to dismiss the "Design Parameters" dialog box.
  19. Go back to the "Symbol" window menu bar, and select File-->Save. We are done with this window, so you can close it if you like.
  20. Go back to the ADS Main Window, Folder View tab. Click on the little arrow to open your resistor cell folder. You can see that your cell now has one view: symbol.
  21. The next few steps prepares a testbench to instantiate it on. From the ADS Main Window menu bar, create a schematic view of a new cell by selecting "File-->New--Schematic...". This will be our top-level cell. Call this new cell "testbench."
  22. Arrange the Main window and the new testbench schematic window side-by-side.
  23. Click, hold, and drag your resistor symbol from the Main window's Folder view tab across your screen to the testbench schematic window. Instantiate your resistor by clicking anywhere on the testbench schematic canvas. Hit the ESC key to leave instantiation mode. Set the resistance to 2.0 ohms.
  24. Create a simple testbench like this:
  25. Click on the Simulate icon or select Simulate-->Simulate or just hit the F7 shortcut key. The first time you simulate, there is a short pause while ADS compiles the Verilog code. It saves the compiled model, so if you don't touch the code, there is no need to re-compile the next time you run it.
  26. From the Data Display window, insert a plot, and select the Vout and branch current (_ub_p_n_i) traces:
    voltage and current through 2 ohm resistor


The next posting, shows you the shared library method. A lot of the steps are the same, except we create separate workspaces for our component library and our test bench, and link the two.

Verilog-AMS and VHDL-AMS are hardware description languages, capable of describing mixed-signal (i.e. analog and digital) hardware. In this series of postings, we’ll be talking about Verilog-A (i.e. the officially defined subset of Verilog-AMS that supports analog) and “VHDL-A” (not officially defined, but defined here as "the parts of VHDL-AMS that supports analog").


ADS support Verilog-A but not VHDL-A so this series of posting will explain how to create a Verilog-A model if all you have to start from is a VHDL-A model. It isn’t a comprehensive Verilog or VHDL reference manual: you can find plenty of those by searching on the Internet. The idea here is to get you started. You’ll have to refer to those other resources for the in-depth information. Another good resource is Best Practices for Compact Modeling in Verilog-A, J. Electron Devices Soc. (Aug. 2015) by Colin C. McAndrew et al. It's not about how to code models in Verilog-A, it's about how to code them well. 


Let jump right in and compare how the two languages express the simplest possible analog component, the resistor:


Code fragment 1a: Resistor in VHDL-A

use electrical_system.all;
entity resistor is
   generic (r: real := 0.0);
   port(terminal p, n: electrical);
end entity resistor;
architecture signal_flow of resistor is
   quantity vr across ir through p to n;
   vr == ir * r;
end architecture signal_flow;


Code fragment 1b: Resistor in Verilog-A

`include "disciplines.vams"

module resistor(p, n);
     parameter real r=0.0;
     inout p, n;
     electrical p, n;
     analog begin
           V(p,n) <+ I(p,n)  * r;

Keywords are in bold. The other identifiers are defined in the code itself, or in the included libraries. For example in both langauges, the identifier electrical is not a keyword: it is defined in a standard library. VHDL-A has:

use electrical_system.all;

…and Verilog-A has:

`include "disciplines.vams"

We won’t go into the contents of these yet. Suffice it to say that they serve a similar purpose, namely to save you from writing the basic “plumbing” required to set up your model.


The two languages diverge in the next lines.


VHDL-A divides the code up into entity and architecture sections. The entity keyword is used to introduce the description of the interface of the component. Here we define ports p and n and a parameter, r. Then, the architecture keyword is used to introduce the description of its internal details. An entity can have more than one architecture ("polymorphism"), so we have to give each one a name, signal_flow in this case, so we can specify which morphology we want to use later on. To implement an architecture with ohmic behavior, we first state explicitly that the voltage V and current I are the across and through variables, respectively, of our ports with:

quantity vr across ir through p to n


(Side note: The terminology “through and across variables” comes from the mathematics of the class of problem that all SPICE-like solvers solve, namely differential algebraic equations or DAEs. In Verilog-A, through and across variables like voltage and currents are called "natures" and a pair of related through and across variables is called a "discipline." Voltage and current natures, plus some other information like tolerances, form the electrical discipline. Confusingly, VHDL-A uses the term "nature" for the pair of natures, whereas Verilog-A uses "discipline.")


Then we specify Ohm’s law, V=IR, for the branch constitutive equation:


 vr == ir * r

The "double equal signs" operator implies the simulator should force the equation to be true for every time step in the simulation: it isn’t a one-time assignment like the a = b in C and Java nor the a := b in Pascal and ADA. (And it isn't at all like the equality test A == B in C and Java.)


In contrast, Verilog-A combines the entity/architecture ideas. The module keyword is used to introduce both interface (the parameter, inout, and electrical lines) and the internals (the block that is bounded by analog begin end ).

The line:

V(p,n) <+ I(p,n) * r; called a contribution statement. It is similar to the one in VHDL with double equals == except that the operator in Verilog-A is <+ . Why <+ ? It's because Verilog-A allows multiple contributions to add to the voltage V(p,n). For example,

V(p,n) <+ V(p2,n2);

V(p,n) <+ V(p3,n3);

…is equivalent to:

V(p,n) <+ V(p2,n2)+ V(p3,n3);


Similar to == in VHDL, <+ in Verilog-A implies the simulator should do whatever it takes (iteration!) to force the equation to be true for every time step in the simulation.


In Verilog-A, there is no need for a line like quantity vr across ir through p to n that we had in VHDL because the so-called access functions, V() and I(), are defined in the `included disciplines.vams header file and thereby associated with any instances of the electrical discipline.


So, that's a component. Part 2 shows you how include the Verilog-A version into ADS create a testbench that instantiates the component, adds some stimulus, and shows you the response.



IC-CAP comes with a powerful API for accessing internal variables and data structures. In what follows, we will illustrate an example of accessing a Setup's Inputs and Outputs to obtain parameter values for the bias conditions for our custom Python measurement routines using the TableVar object.


What might be a typical approach?
When configuring a source measurement unit (SMU) to define start and stop voltages for a measurement sweep, you might use multiple setup variables stored in the Setup Variables table like this:


Setup Variables in IC-CAP


Then use some Python statements to read the parameter values from the table.


# query setup variables
vg_start = SVar("vgstart").get_val()
vg_stop = SVar("vgstop").get_val()
vg_step = SVar("vgstep").get_val()
vg_pts = SVar("vgpts").get_val().split(".")


But instead, I'm going to show you how to read the measurement configuration values entered in the Measure/Simulate tab and use these values in your Python code.


Measure/Simulate Tab

This allows you to set parameters in the Measure/Simulate tab just like when using the native IC-CAP instrument drivers in normal Measure mode.  Now you can use the built-in IC-CAP user interface to modify the parameters. In using this method, you will have to select the Extract/Optimize tab, select the transform and then click the Execute button to invoke your custom Python transform.




If you have not already setup your Python virtual environment and have IC-CAP installed, please refer to my previous article Measuring Sub-Threshold MOSFET Characteristics Using a Quasi-Static I-V Method.


Depending on whether you are using an older Agilent 4156B/C or a newer Keysight E5270B for your SMU, you may download and open one of the IC-CAP model files included at the end of this post.

  • py_api_demo_5270_v.2.0.mdl
  • py_api_demo_4156_v.2.0.mdl 

The Python code included for the E5270B can be applied to the B1500A with almost no modification as their command sets are very similar. Similarly, the code for the 4156B/C can be used with a 4142B with minor modification because we are using the US42 command mode using Flex commands. 


I recommend saving the *.mdl to C:\Keysight\ICCAP_2018\examples\python.  


Load the IC-CAP Model File


Assuming you saved the model file to the location above, click File/Examples... and select: 



Go to the DUT/Setup called dc/idvg_quasi. Select the Measure/Simulate tab and observe the settings for the Inputs and Outputs. Notice that we have set conditions to make the Id vs. Vg measurement using IC-CAP's built-in drivers. Now select the Setup Variables tab and review the variable table.


Name            Value               Comment
drain           1                   Drain SMU Channel
gate            2                   Gate SMU Channel
source          3                   Source SMU Channel
substrate       4                   Substrate SMU Channel
interface       GPIB0:17::INSTR     VISA instrument resource
visaResponse                        VISA response string
error_status    32,48,128           Status bytes to check for errors
error_command   ERR?,EMG?,ERRX?     Commands to process error events
hold_bias       0.0                 Bias applied during hold time period
hold            0.5                 Hold time before first measurement, in seconds
delay           0.1                 Delay time
step_delay      0.2                 Step delay time


Certain measurement configuration parameters like hold, delay and step delay parameters would normally be set in the Instrument Options of the IC-CAP instrument driver, but because we have not configured an IC-CAP instrument driver, we will read these parameters from the Setup Variables instead.

# add variables for instrument options and other variables
polarity = MVar("POLARITY").get_val() 

# get setup variables



In our previous IC-CAP model file, the bias conditions for the gate sweep, drain, source and substrate values were stored in the Setup Variables table then read from table and assigned to local variables.  In this latest version we have removed these values from the SVars table and will define them in the _init Python transform, as shown below.


Note: You may need to change the SMU channel assignments ('drain', 'gate', 'source', 'substrate') to match the configuration of your instrument. For example, your Drain SMU Channel may be slot 5, not slot 1.


Exploring the _init transform


Select the Extract/Optimize tab and then select the _init transform.


New initialization code has been added to the _init transform relative to previous versions of the example mdl files. In this example, we will use functions provided by IC-CAP to query the settings of the Inputs from the user interface in the Measure/Simulate tab to set the measurement conditions for our quasi-static "Id vs. Vg" MOSFET measurement. 


idvg_quasi Init transform


The following Python local variables in our _init transform will be used to store the measurement parameters from the defined Inputs.

Variable Name   Description
vgstart         Gate sweep start voltage
vgstop          Gate sweep stop voltage
vgstep          Gate step size
vgpts           Gate points
igcompl         Gate current compliance
vdvalue         Drain voltage (constant)
idcompl         Drain current compliance
vsvalue         Source voltage (constant)
iscompl         Source current compliance
vbvalue         Bulk voltage (constant)
ibcompl         Bulk current compliance


We will use these variables the instrument specific SMU commands to programmatically control the quasi-static sweep. 


Setup Object and Methods


Instead of using the IC-CAP function call SVar(“Varname”).get_val(), we will be using the TableVar object provided by IC-CAP's Python environment.  Thanks to the iccap python module, we can programmatically define, manipulate and query DUTs, SetupsTransforms, Inputs and Outputs.  In order to access the TableVar object of the Input and query its values, we will need a reference to its parent Setup object.


Let’s define an object representing the idvg_quasi Setup by name. You will find these two statements in the _run transform.


s = Setup("/py_api_demo_5270/dc/idvg_quasi")
setupName = s.get_fullname()


We can now query this object for its properties (assuming debug = 1)


# query setup
if debug: print "fullname:",s.get_fullname()
if debug: print "name:",
if debug: print "mytype:", s.mytype


Setup query response:

fullname: /py_api/demo/5270/dc/idvg_quasi

name: idvg_quasi

mytype: Setup


With a for loop, we can query the sweeps defined for the Setup.  


# query sweeps with order
if debug:
   for sweepid.range(1,2):
      inputobj = set.get_sweep(sweepid)
      if inputobj == None: break
      print "Sweep ", sweepid, s.get_fullname()

In this case, we have only a 1st order sweep.

Sweep query response:

Sweep 1 /py_api_demo_5270/dc/idvg_quasi


Now, I will show you how to query some of the other parent/child hierarchies so that you get an idea of the type of information that is available to your Python programs.


We can query the setup object for children objects in its hierarchy.


# query child objects and type
if debug:
   print "child objects of Setup:"
   for objchildtype in s.childtypetuple:
      print objchildtype
      for objchild in s.get_child_objects(objchildtype):
         print " ",


Child object query response:

child objects of Setup:




































One may query any Setup for these properties and then assign them to local variables for use in your Python code.


Input/Output Objects


We will read the Input values from the IC-CAP TableVar objects and assign them to a Python string variable. Let's start with the Sweep parameters for the vg source.


vg = Input(setupName + "/vg")


The full statement will expand to Input("/py_api_demo_5270/dc/idvg_quasi/vg").  Like the Setup object,  the Input object also provides functions for returning information like full_name, name, and type.


# build full name for the sweep input
sweepName = vg.get_fullname()


Using the Input object vg, we set a variable sweepName for the fullname returned from the Input, we can read each of the vg Input's sweep TableVar parameters including: Compliance, Start, Stop, Step Size and # of Points.


igcompl = TableVar(sweepName + "/Compliance")
vgstart = TableVar(sweepName + "/Start")
vgstop = TableVar(sweepName + "/Stop")
vgstep = TableVar(sweepName + "/Step Size")
vgpts = TableVar(sweepName + "/# of Points")


We now have pointers to the following:




We can now query various properties of our newly created TableVar object.

# query TableVar
if debug: print "fullname :", igcompl.get_fullname()
if debug: print "name :",
if debug: print "mytype :", igcompl.mytype
if debug: print "value :", igcompl.get_val()
if debug: print

# query TableVar
if debug: print "fullname :", vgstart.get_fullname()
if debug: print "name :",
if debug: print "mytype :", vgstart.mytype
if debug: print "value :", vgstart.get_val()
if debug: print


With the debug flag enabled, we obtained the following results in the IC-CAP Output window:

full_name : /py_api_demo_5270/dc/idvg_quasi/vg/Compliance
name : Compliance
mytype : TableVar
value : 0.1

full_name : /py_api_demo_5270/dc/idvg_quasi/vg/Start

name : Start

mytype : TableVar

value : 0.0




Using the TableVarObject.get_val() method, we define local variables and cast the returned string to an appropriate float or integer types for later calculations.


# set gate sweep parameter values
vg_start = float(vgstart.get_val())
vg_stop = float(vgstop.get_val())
vg_step = float(vgstep.get_val())
ig_compl = float(igcompl.get_val())
vg_points = int(vgpts.get_val().split('.')[0])


The type or polarity (NMOS or PMOS) is read from the Model Variable Table as specified in the beginning of the _init transform variables presented above. If its value is PMOS, the values for the gate and drain biases are multiplied by -1, which will appropriately handle the device polarity. 


# get the device polarity from the model variable table
polarity = MVar("POLARITY").get_val()


We can use the value of polarity to invert the sign on Vgs  appropriately for the device type.


if polarity == "PMOS":
   vg_start *= -1
   vg_stop *= -1
   vg_step *= -1


Querying Constant Input Variable Properties


What if the input variable is not swept but is a constant? The Input vd is defined as a CON (constant source type) so the only available properties are the bias value and the compliance.


vd = Input(setupName + "/vd")


The input vd is queried from just like the vg sweep variable.


# build full name for the constant input
constName = vd.get_fullname()


Using the Input object vd we set a variable constName for the fullname returned from the Input, we can read each of the vd Input's TableVar parameters including: Compliance and Value.



idcompl = TableVar(constName + "/Compliance")
vdvalue = TableVar(constName + "/Value")


Again, we may query various properties of our TableVar object. A couple examples are provided as follows.


# query TableVar
if debug: print "fullname :", idcompl.get_fullname()
if debug: print "name :",
if debug: print "mytype :", idcompl.mytype
if debug: print "value :", idcompl.get_val()
if debug: print

# query TableVar
if debug: print "fullname :", vdvalue.get_fullname()
if debug: print "name :",
if debug: print "mytype :", vdvalue.mytype
if debug: print "value :", vdvalue.get_val()
if debug: print


Executing the code statements above with the debug flag enabled will produce the following in the IC-CAP Output window:


full_name : /py_api_demo_5270/dc/idvg_quasi/vd/Compliance

name : Compliance

type : TableVar

value : 0.1


full_name : /py_api_demo_5270/dc/idvg_quasi/vd/Value

name : Value

mytype : TableVar

value : 3.0


Using the TableVarObject.get_val() method, we define local variables, having cast the returned string to the appropriate float or integer types for later calculations.

# set drain parameter values
id_compl = float(idcompl.get_val())
vd_value = float(vdvalue.get_val())


Again we can use the value of polarity to invert the sign on Vds appropriately for the device type.


if polarity == "PMOS":
   vd_value *= -1


A similar approach is implemented to query the constant type inputs vs and vb. 




With the this modified _init transform, we can now read the bias settings and compliance settings from the Measurement/Simulate tab for the idvg_quasi Setup.




To run this, we will not click the Measure button in Measure/Simulate tab, but instead navigate to the Extract/Optimize tab, select the _run transform and click the Execute button to perform the measurement.


Using these powerful IC-CAP objects and functions from the API, you can easily implement custom measurement and extraction routines to provide familiar usage modalities for your engineers who are familiar with the standard IC-CAP user interface.  I hope the information presented here provides some insight to the internal objects, functions and data types available to you through the Python environment within IC-CAP and will help you in implementing your own custom measurements.  More detailed information on all the information presented here can be found in the IC-CAP Python programming examples or the IC-CAP User's Manual.



IC-CAP 2018 user-manual and documentation

Example model file "py_api_demo.mdl" under Macros

Related Links


I'm sure most of you have heard about WBG semiconductors recently; specifically Silicon Carbide (SiC) and Gallium Nitride (GaN).  The primary, but not exclusive, context was probably related to Power Electronics.  So let's explore at a high level why this technology is increasingly getting more and more attention in the Industry.  It basically comes down to simply 3 things – smaller, faster and more efficient electronics.


WBG can operate at higher temperatures, handle 10x higher voltages and eliminate up to 90% of the power losses in electricity transfer compared to current technology.  This will result in lower cost electronics and save billions of $s in energy.


Power supplies including adapters used to charge laptops, cell phones and tablets use at least 2% of all US energy. WBG can make chargers 3-5 x smaller and cut the heat loss by 75-80%.  This is equivalent to the power generated by 3 Hoover Dams a year!


LEDs use WBG semiconductors to produce 10x more light per watt of energy than incandescent bulbs and last 30x longer.


At today's energy costs WBG semiconductors are expected to save $250 Billion in cumulative energy costs by 2030.  As more renewable power is connected to the grid we will rely more on WBG electronics to transfer electricity. WBG semiconductors will reduce power losses in transmission by up to 75% leading to smaller power stations and lower cost for renewable energy.  This in turn will lead to lower cost electronics and billions of dollars in energy savings.


For Power Devices:

The advantages of SiC over Si for power devices include lower losses for higher efficiency, higher switching frequencies for more compact designs, robustness in harsh environments, and high breakdown voltages.  SiC also exhibits significantly higher thermal conductivity than Si, with temperature having little influence on its switching and thermal characteristics.  This allows operation of SiC devices in temperatures far beyond 150° C, the maximum operating temperature of Si, as well as a reduction in thermal management requirements for lower cost and smaller form factors.


For RF Devices:

GaN offers key advantages over silicon.  The high power density of GaN leads to smaller devices as well as smaller designs due to reduced input and output capacitance requirements, an increase in operational bandwidth, and easier impedance matching.  GaN's high breakdown field allows higher voltage operation and also eases impedance matching.  The broadband capability of GaN devices provides coverage for a broad frequency range to support both the application's center frequency as well as the signal modulation bandwidth.  Additional advantages of GaN include lower losses for higher efficiency, and high-temperature operation (in the case of GaN on bulk-GaN substrate).


In conclusion, WBG semiconductors will make the next generation of electronics smaller, faster and more efficient – there is little question on this point. This will have huge ramifications for the entire energy/electronics industry – from power stations to renewable energy to electric automobiles to personal computing and communication. All aspects of our lives will be positively impacted by this revolution.  The question for us is not if, but when, the current design methodology and models will hit the proverbial brick wall? 

Please register for a webinar I'm presenting Sept 6th, 2018 at 1PM EDT. The title is Designing Switched-Mode Power Supplies in the High di/dt Era. I hope you will attend!

While IC-CAP is equipped with a powerful library of transforms and examples for performing model parameter extraction, we may extend this power using the built-in Python support in IC-CAP to access external Python libraries, especially when developing new behavioral models or implementing custom analysis routines.


In my previous article, I showed how to implement a quasi-static measurement in a series of IC-CAP transforms, demonstrating the flexibility of the data handling capabilities in IC-CAP.  Python modules like and modules extended the data parsing using external libraries. In this article, I will show you how you can also easily implement custom parameter extraction routines using Python in IC-CAP. Leveraging the work I presented in my previous blog entitled "Measuring Sub-Threshold MOSFET Characteristics Using a Quasi-Static I-V Method", I'll discuss a custom Python transform for extracting the Vth parameter (gate threshold voltage) on the quasi-static Id vs. Vg sweep data of a p-channel MOSFET.


We will be implementing the ICON (constant current) method for extracting Gate Threshold Voltage (Vth) from the Id vs. Vg data. This is a popular method due to its simplicity. Other popular methods ( i.e. Id_sat or Max_Gm methods ) can be similarly implemented using custom Python classes and functions.




Depending on whether you happen to have an E5270B (or B1500A) or an older 4156C, you might prefer to download the complete IC-CAP model file py_new_api_basic_demo_5270.mdl or py_new_api_basic_demo_4156.mdl attached at the end of this post.

E5270BAgilent 4156C


Each model file contains the complete Python source code used to implement the quasi-static measurement. I recommend saving these *.mdl files to C:\Keysight\ICCAP_2018\examples\python, as I described in my previous article Measuring Sub-Threshold MOSFET Characteristics Using a Quasi-Static I-V Method. In these .mdl files we previously created the global arrays vvalue[ ] and ivalue[ ] in the _run transform for storing our forced voltage and measured current data values. In the _calc_vth transform we will access these arrays to extract the Vth value by calling an external Python script also attached below. You should copy the to your C:\Users\<username>\iccap\python directory or the directory specified by the environment variable ICCAP_USER_PYTHON_PATH.


Load the IC-CAP Model File


Assuming you saved the model file to the examples folder (C:\Keysight\ICCAP_2018\examples\python), click 'File/Examples...' and select: python\py_api_new_basic_demo_5270.mdl. (or *_4156.mdl).


Select the idvg_quasi Setup, go to the 'Extract / Optimize' tab and then select the _run transform.

The following arrays are defined to store the local I-V measured data:


# global list of values
vvalue = []
ivalue = []



Notice that the global namespace is set in the Function field by selecting PythonGlobal. This gives us access to the ivalue[] and vvalue[] along with vg_points (from the vgpts Setup Variable) which will be used in the extract_vth_cc function _calc_vth() below.

Go to the end of the _run transform and uncomment the following lines of code:


# calculate vth
if bOk: iccap_func("_calc_vth","execute")



Select the idvg_quasi Setup, go to the 'Extract / Optimize' tab and then select the _calc_vth transform.


from extract_vth_cc import * 
# Calculate Threshold voltage for IdVg measurement

# Uses _extract_vth which implement Icon method (IdVg@Vb=0)
# Device parameters

# W = channel width (um)
# L = channel length (um)
# M = channel mask multiplier
# deltaW = channel width variation
# deltaL = channel length variation 
# get W, L from Model Variables
W = float(MVar("W").get_val())
L = float(MVar("L").get_val())
M = 1.0
icon = 1E-8
deltaW = 0
deltaL = 0
if debug: print "W = {} L = {} M = {} icon = {}
                 deltaW = {} deltaL = {}\n".format(W,L,M,icon,deltaW,deltaL)

if polarity == "PMOS":
   icon *= -1
# get Id[] Vg[] from _run
ids = ivalue[:vg_points]
vgs = vvalue[:vg_points] 

# get Id_Vg @ Vb=0
evt = extract_vth_cc(ids, vgs, W, L, M, deltaW, deltaL, icon)
vth = evt.calculate() 

print "vth = ", vth


The _calc_vth transform instantiates the extract_vth_cc class and initializes it by passing the measured data arrays ids and vgs along with the device geometry W, L, deltaW, deltaL and icon parameter (constant current value for extracting the gate threshold voltage). Once the extract_vth_cc instance is created, the calculate() function (or method) is called to return the Vgs that gives the current closest to the icon value.


Open the Python source code in your favorite editor.


Review the implementation of the extract_vth_cc class and calculate method. source code listing

import numpy as np
from iccap import MVar
# Global variables
# enable/disable debug prints
debug = int(MVar("debug").get_val()) 

# extract_vth_cc script for extracting threshold voltage
class extract_vth_cc:    
   def __init__(self,id,vg,W,L,M,deltaW,deltaL,icon):
       Initialize extract_vth_cc class.

       This function initializes the class used to extract Vth using
       the ICON method.

          self:      This instance of the extract_vth_cc class.
           id:        Drain current array values.
           vg:        Gate voltage array values.
           W:         Device channel width.
           L:         Device channel length.
           M:         Channel mask multiplier.
          deltaW:    Channel width variation.
           deltaL:    Channel length variation.
           icon:      Constant current to extract Vth.

       Returns:       instance of class

       """ = id  # a list [] = vg  # a list []
       self.W = W
       self.L = L
       self.M = M
       self.deltaW = deltaW
       self.deltaL = deltaL
       self.icon = icon
       # calculate and return Vth
       def calculate(self):
          Calculates the gate threshold voltage.

          This function extracts the extrapolated Vth value by using
          the ICON method.

             self:      This instance of the extract_vth_cc class.


             Vth:       Interpolated gate threshold voltage at icon.

        # initialize local variables 
        vth =1E-30
        type = 1       # nmos        
        vg =
        id =

        # create numpy array and initialize
        nid = np.zeros([len(id),1])
        # copy id to numpy array and flatten
        nid.flat[:] = id

        # compute the id reference value from device geometry
        iref = self.icon * self.M * (self.W - self.deltaW)/(self.L - self.deltaL)
        if debug: print "iref = {}".format(iref)
        # return the index closest to the reference value
        idx = (np.abs(nid - iref)).argmin()
        if debug: print "idx = {} id = {}".format(idx, id[idx])        
        # check if vg is negative - PMOS device type
        if vg[idx] < 0:
             type = -1   # pmos
        # return vg @ iref
        Vth = vg[idx]
        return Vth


Notice the use of Numpy arrays in the code above. The Numpy python module provides very convenient methods for processing array variables. (You'll never go back to PEL...) The zeros method creates a new Numpy array the length of the data ("len(id)") and initializes all values to 0.  Once the Numpy array is created and initialized, the flat method is used to iterate the 1D array while copying the id array values to the local nid array. The algorithm scales the constant current icon based on device geometry to calculate a reference current iref. Using this iref value, the index of the data point is returned which is closest to our target. This resulting index is used to return the value of the element of the vg array which is the Vth value.




You can quickly create reusable custom extractions or analysis routines using Python to start building your own modeling library. I hope this quick example of extracting gate threshold voltage will serve as a starting point for developing your own custom routines for use in IC-CAP. In follow on articles, I will demonstrate additional features of IC-CAP to streamline and customize your Python transforms using the IC-CAP's built-in user interface elements and TableVars to allow using parameter inputs in the Measure / Simulate tab to provide user-entered data to your Python transforms.


Related Links


Measuring Sub-Threshold MOSFET Characteristics Using a Quasi-Static I-V Method

How to Extract Threshold Voltage of MOSFETs

Python Programming Integration with IC-CAP

Negative Bias Temperature Instability (NBTI) refers to a positive shift in threshold voltage in CMOS devices, hastened by negative gate voltages and high temperatures.  Resulting in lower device currents and lower transconductances, NBTI presents a device reliability concern, especially as gate-oxides become thinner and as device geometries continue to become smaller.  Reliability engineers have tried to characterize this phenomenon by applying a series of biases to stress the device for increasing time intervals, while measuring device parameters such as gate threshold voltage (Vth) after each stress period. In this way, they will observe an accelerated lifetime of the part.  Due to a self-healing process known as recovery, the device can appear to partially return to its pre-stress state, causing the test results to often overestimate the lifetime of the device.  To compensate for NBTI recovery, engineers at X-FAB Semiconductor proposed a Recovery Correction Method (RCM) [1] when measuring NBTI induced threshold voltage degradation in high-K CMOS transistors. The RCM method could be an excellent alternative to using the more typical extrapolation of Vth degradation model data to estimate NBTI lifetime. With RCM, the effect of NBTI is modeled using a curve fitting technique to estimate the lifetime of the device, even in the presence of recovery. Thus, one could eliminate the need for expensive and specialized hardware setup and complicated triggering associated with popular fast-IV pulse NBTI systems [2]. 


Intrigued by the potential of RCM, I wanted to explore device recovery times using a quasi-static DC-IV measurement of the sub-threshold region. To properly measure the influence of delays between stress and measurement, I needed very accurate measurements for both p-channel and n-channel MOSFET devices. Shown in Figure 1 below, each quasi-static measurement will be nestled between stress events.  By varying the time between a stress event and a measurement, and further modifying the measurement times during the Vth measurement, we can explore the nature of the NBTI recovery more precisely.  


Figure 1. NBTI Stress-Measurement timing diagram. [4]


Quasi-static DC IV

I performed multiple measurements on p-channel devices while adjusting delay time and the step delay of the sweep as shown in Figure 2. This measurement method is commonly known as quasi-static DC IV


fig.1. Quasi-static Measurement

Figure 2. Quasi-Static Bias for Each Step of Vg During the Vth Measure Cycle in Figure 1.


Quasi-static measurements can more accurately characterize the energy levels of interface traps at the Si/SiO2 interface, commonly understood as the underlying mechanism for NBTI. It has numerous advantages over a standard curve tracer sweep.

  1. Multiple samples used for averaging, filtering out noise.
  2. Staircase sweep mode performed by series of high-speed single spot measurements.
  3. Ability to set current and power compliance during each measurement point during the voltage sweep.
  4. Ability to set a user defined ramp rate (dV/dt) of the voltage sweep.
    ( Sampling interval = delay + step delay = dt )


Low-voltage, sub-threshold measurements of small geometry MOSFETs are difficult to measure using conventional NBTI measurements. However, using Python, I was able to program Keysight’s E5270B Precision IV Analyzer with multiple high-resolution SMUs to perform accurate current measurements over swept applied voltages with varying source and measurement delays, to better characterize the recovery mechanism in these devices. 


Writing a Set of Custom Transforms in IC-CAP


The programming guide for the E5270B lists a quasi-static pulsed measurement mode built into the command set of the instrument. [5] That’s great; however, it requires a (stop - start) voltage > 10 V. Unfortunately, that would damage my devices, which have a maximum voltage rating of only +/- 3.3 V. I therefore determined that I needed to create my own custom quasi-static measurement routine by implementing a series of custom IC-CAP transforms in Python. Since I would likely switch to the Keysight B1500A Semiconductor Device Parameter Analyzer in the future, my solution must support diverse instrument choices. The specific instrument control commands may vary slightly from one instrument to another.


Leveraging the work I presented in my previous blog entitled "Using a Python VISA API within IC-CAP for Instrument Control", I'll present custom transforms for a quasi-static measurement of an Id vs. Vg sweep on a p-type MOSFET.

I hope you find this example helpful in developing your own application-specific measurements.




The complete IC-CAP model file py_new_api_basic_demo.mdl with the Python source code used to implement the quasi-static measurement is attached at the end of this post. I recommend saving this *.mdl to C:\Keysight\ICCAP_2018\examples\python


  1. Install IC-CAP 2018, under Windows 7 or 10 in the default installation directory; typically C:\Keysight\ICCAP_2018.
  2. Install the Keysight IO Libraries 17.x software. The visa32.dll and visa64.dll dynamic link libraries are located in the C:\Windows\system32 directory.
  3. Configure and confirm communication with your instruments using the 'Keysight Connection Expert' software.
  4. Install and configure your Python virtual environment, and define the appropriate IC-CAP environment variables. Please refer to my previous blog article Extending the Power of IC-CAP Software with Python -- PyVISA Control for a detailed explanation on creating Python virtual environments and loading external Python libraries. Change references to the virtual environment name icenv to icenv2018 (or a name of your choice..) and replace IC-CAP_2016_01 with IC-CAP_2018.
  5. Download the and scripts attached to this post.The utility described in my previous article "Using a Python VISA API within IC-CAP for Instrument Control" is used to manage communication with the instrument. The script called enables parsing and processing ASCII data retrieved from the instrument's data buffer.
  6. Confirm you have the environment variable ICCAP_USER_PYTHON_PATH defined in your Windows environment: 

    Tools> Programs> Advanced System Settings: Environment Variables. The location specified by this variable is added to the directories IC-CAP will search to find python scripts. (For an IC-CAP installation, the directory containing the 'factory' python files is typically: C:\Keysight\ICCAP_2018\iccap\lib\python).

  7. The and files should be placed in the directory specified by the variable ICCAP_USER_PYTHON_PATH. 

  8. Several Python module libraries are referenced in the _run transform to provide support for parsing arrays, writing CSV files and other functions. You will need to install these modules in the Python virtual environment using PIP (package manager for Python) .


The required Python modules include the following: 

  • pathlib2 - a backport of the new features in Python 3 for creating and managing file paths,
  • csv - a package for writing and reading comma separated values - already included in Python27
  • imp - a package for dynamically importing Python modules - already included in Python27


Activate and verify the Python 2.7 virtual environment by checking the system path 'sys.path' and system prefix 'sys.prefix.' To do this, open a Windows command shell and perform the following steps:


Activate the (icenv2018) virtual environment

C:\Users\username> workon icenv2018

(icenv) C:\Keysight\IC-CAP_2018\tools\win32_64>

Change the directory to virtual env (icenv)

C:\Users\username> cdvirtualenv

Check the Python interpreter version for (icenv2018)

(icenv) C:\Users\username\Envs\icenv> python -V

The version should return:

Python 2.7.13


Start the interactive Python interpreter for the virtual environment.

(icenv) C:\Users\username\Envs\icenv2018> python

You should see something like the following:

Python 2.7.13 (default, Mar 29 2017, 13:31:10) [MSC v.1900 64 bit (AMD64)] on win32

Type "help", "copyright", "credits" or "license" from more information.


Now, enter the following commands at the Python >>> interactive prompt:

>>> import sys 
>>> print sys.prefix

You should see something like the following:


Now type:

>>> print sys.path

You should see something like the following:


To exit the virtual Python interpreter type:

>>> quit()

(icenv) C:\Users\username\Envs\icenv2018>

Leave the virtual environment by typing:

(icenv) C:\Users\username\Envs\icenv2018> deactivate



This completes the prerequisites.Your icenv virtual Python environment is configured and ready to be used from IC-CAP. In the following section, I will provide a step-by-step guide on the construction and use of this example.


Load the IC-CAP Model File


Assuming you saved the model file to the examples folder (C:\Keysight\ICCAP_2018\examples\python), click 'File/Examples...' and select: python\py_api_new_basic_demo_5270.mdl. (The model file is attached at end of this post along with a version supporting the Agilent 4156 Semiconductor Parameter Analyzer.)




Before starting to write the code it is convenient to add some User variables to IC-CAP's Model Variables Table to aid in configuring the device parameters and measurement conditions.


Click on the 'Model Variables' Tab





Note the 'debug' variable defined in the 'Model Variables' Table.

Name            Value               Comment
debug           1                   Enable/disable global debug print statements

This enables the reporting of useful diagnostic information.


Select the idvg_quasi Setup, go to the 'Measure / Simulate' tab and observe the Inputs vd, vg, vb, and the Output id, defined to contain the data we will be acquiring from our transforms.



The vg Input has the Sweep Type defined to be Lin(linear). We will build an equivalent data vector for the vg Input using the _meas_iv transform.


Select the 'Setup Variables' tab under the setup idvg_quasi.

 idvg_quasi setup variables table


Notice the following variables defined in the idvg_quasi 'Setup Variables' Table.

Name            Value               Comment
drain           1                   Drain SMU Channel
gate            2                   Gate SMU Channel
source          3                   Source SMU Channel
substrate       4                   Substrate SMU Channel
interface       GPIB0:17::INSTR     VISA instrument
visaResponse                        VISA response string
error_status    32,48,128           Error codes
error_command   ERR?,EMG?,ERRX?     Command to process errors
hold_bias       0.0                 Bias applied during hold time period
hold            0.5                 Hold time before first measurement, in seconds
delay           0.1                 Delay time
step_delay      0.2                 Step delay time
vgstart         0.0                 Gate sweep start voltage
vgstop          3.0                 Gate sweep stop voltage
vgstep          0.1                 Gate step size
vgpts           31                  Gate points
igcompl         1E-1                Gate current compliance
vdvalue         3.0                 Drain voltage (constant)
idcompl         1E-1                Drain current compliance
vsvalue         0.0                 Source voltage (constant)
iscompl         1E-1                Source current compliance
vbvalue         0.0                 Bulk voltage (constant)
ibcompl         1E-1                Bulk current compliance


These set the measurement parameters that will be sent to the SMU.  You may need to change the SMU channel assignments ('drain', 'gate', 'source', 'substrate') to match the configuration of your instrument.  For example, your 'drain' (Drain SMU Channel) may be slot 5, not slot 1, etc.


In the 'Setup' named idvg_quasi, select the 'Extract/Optimize' tab. 


The following transforms are defined:

  1. _run - the main Transform that calls other Transforms and provides functions for processing the measurement data and writing data files.
  2. _init - reads values from the IC-CAP Setup Variable Table ("SVar") and initializes parameters for the measurement
  3. _setup_iv - sends commands to configure the instrument for upcoming measurement
  4. _meas_iv - sends commands to instrument to perform actual measurement, including channel specific ranging, bias application, compliance and measurement triggering
  5. _close - turns off biases, disconnects outputs, and closes the VISA session.


Note: The _run Transform contains code calling the '' script, which will activate the virtual python environment. Modify the path defined for the 'activate_this' variable, to the correct setting for the environment.


idvg_quasi setup _run transform


Notice the 'Function' definitions for the python transforms, _run, _init, etc. are set to 'PythonGlobal' Information, including the data and variables, will be shared in the global transform space. To perform the quasi-static DC measurement, select the _run transform from the 'Extract/Optimize' tab and click Execute


The _run will call the other transforms in the order needed. The resultant data can be plotted as shown in the 'ThreshholdVoltageIdsvsVgs' plot under the idvg_quasi Setup.


# Perform measurement
# open communications interface 
vl = visaOpenSession(intr)
if debug and vl: print "VISA resource: {} found".format(intr) 

# forward call additional transforms
if bOk: iccap.iccap_func("_init","execute")
if bOk: iccap.iccap_func("_setup_iv","execute")
if bOk: iccap.iccap_func("_meas_iv","execute") 

# zero channels and close GPIB interface
if bOk: iccap.iccap_func("_close","execute") 

if debug: print "_run - complete bOk: ", bOk


There are several necessary lines in the _run script that import the correct Python modules for use in the other transforms. The first two statements in the script below activate and execute the Python interpreter in the virtualenv named icenv2018 that was previously installed. (As mentioned earlier, the path to the '' script should be modified to be correct for the current user environment)

# # # Main execute file for IdVg measurement

activate_this = "/Users/<USERNAME>/Envs/icenv2018/Scripts/"
execfile(activate_this, dict(__file__=activate_this)) 
# imports
from iccap import icfuncs as f, SVar, MVar, get_dataset, set_dataset, Setup, TableVar
from pyvisawrapper import *
from numpy import *
import itertools
import pathlib2 as pl
import csv
import math
import time
import imp


The imp package has been added to aid in re-initializing our external modules while debugging.  IC-CAP imports these external modules only once when it starts the Python environment. If you make changes to the module code in an external editor, the changes will not be updated until the next time you start IC-CAP. To work around this issue you can use the imp.reload( ) function to reload the modules during execution of the _run transform.



# Global variables
pv = imp.reload(pv)    
dp = imp.reload(dp)    
from pyvisawrapper import *    
from dataparser import * 




Here we are setting some global variables by reading the Model and Setup Variable Table variables defined in an earlier step. Via this method, one can quickly change global values in the appropriate table, rather than needing to modify Python code in each transform individually. 


# defaults from the model variables table

# get debug enable/disble
debug = int(MVar("debug").get_val())   # enable debug print



The MVar command gets the values from the 'Model Variables' Table.  The get_val( ) function returns the value of the variable in the table, and assigns it to the specified local variable. I usually specify variables (and their values) in the Model Variable Table that apply to all Setups in the IC-CAP model file, which is the case here.


# get channels from setup variables table 
drnSMUChan = SVar("drain").get_val()
gateSMUChan = SVar("gate").get_val()
srcSMUChan = SVar("source").get_val()
subSMUChan = SVar("bulk").get_val() 

intr = SVar("interface").get_val()

rsp = SVar("visaResponse")
stat = SVar("error_status").get_val().split(",")
err = SVar("error_command").get_val().split(",")


The SVar command gets the values from the 'Setup Variables' Table. I usually specify values in the Setup Variables Table that apply to a particular Setup. This supports the case where you might want to have different channels defined for other measurements or even specify a different instrument in the 'interface' variable for a different measurement setup.



Open _init transform


The _init transform reads the Setup Variable table values and assigns them to local variables to be used later when updating parameters for the instrument commands.




The type or polarity (NMOS or PMOS) is read from the Model Variable Table. If its value is PMOS, the values for the gate and drain biases and compliance are multiplied by -1, which will provide negative bias to the device terminals.


# Initialize global variables and parameters for IdVg_Quasi

if debug: print "_init - enter: bOk =", bOk 

# add variables for sweep source and other variables
polarity = MVar("POLARITY").get_val() 

# get setup variables

if polarity == "PMOS":    
   vg_start *= -1    
   vg_stop *= -1    
   vg_step *= -1    
   ig_compl *= -1
if debug:
   print "vg start = {} vg stop = {} vg step = {}".format(vg_start, vg_stop, vg_step)

vg_points = int(SVar("vgpts").get_val().split('.')[0]) 

if polarity == "PMOS":    
   vd_value *= -1    
   id_compl *= -1

if debug: print "vd value = {}".format(vd_value) 


if polarity == "PMOS":    
   is_compl *= -1    
   ib_compl *= -1 

if debug: print "_init - exit:", bOk



Open _setup_iv transform


The _setup_iv transform sends commands to initialize the instrument and set specific features.




The FMT11 command sets the data return format to ASCII. We will discuss that in detail further below, when we look at how to parse the data returned from the instrument's data buffer. (See the E5270B Programming Manual for details)


# set data format to binary 4 bytes 
cmd = "FMT11"
if bOk: bOk = visaWrite(vl, cmd, 10000, stat, err) 



Open the _meas_iv transform


The _meas_iv transform performs the quasi-static measurement.




# perform quasi-spot sampling measurement with auto ranging 
for i in range(0, vg_points):    
   # calculate next voltage step    
   value = float(vg_start) + (float(vg_step)*i)     

   # append value to vvalue[] list    

   # set wait hold, delay to 0.0000 and step_delay will be handled by     
   cmd = "WT " + str(hold) + "," + str(delay) + "," + str(step_delay)    
   if bOk: bOk = visaWrite(vl, cmd, 10000, stat, err)     

   # set quasi-pulse parameters start = stop    
   cmd = "WV" + gateSMUChan + ",1,0," + str(value) + "," + str(value) + ",1," + str(ig_compl)    
   if bOk: bOk = visaWrite(vl, cmd, 10000, stat, err)     


   # trigger the measurement    
    cmd = "XE"    
    if bOk: bOk = visaWrite(vl, cmd, 10000, stat, err)     



For each iteration of the 'for' loop, the WT command sets the Hold, Delay and Step Delay time of the measurement as depicted in Figure 2 above. The Start and Stop value for the WV staircase sweep command is set to the same Vg value. This effectively performs a single point sweep at each specified Vg step. The XE command triggers the measurement. Once complete, the data value is retrieved from the instrument data buffer by calling the get_Measured_Data( ) function. (The get_Measured_Data() function is defined in the _run transform).


We previously created the global arrays vvalue[ ] and ivalue[ ] for storing our forced voltage and measured current data values.


# global list of values
vvalue = []
ivalue = [] 



The get_Measured_Data( ) function retrieves the data from the instrument data buffer and uses the  script to process the returned data packets.


# measurement complete so get the data     
if bOk: bOk = get_Measured_Data(i)


The data format for the E5270B is formatted based on the FMT command. This discussion assumes a FMT11 

command was sent to the instrument in the _setup_iv transform.


The ASCII data values are expected to be in returned in the following format: (FMT11 is 13 digits data with header)



   A: Status. One character.
   B: Channel number. One character.
   C: Data type. One character.
   D: Data. Twelve digits or 13 digits.


An example of the data returned from the instrument is  NAI+1.23684-E09.  


Within the _run transform, the get_Measured_Data() function calls get_ASCII_Data_Info( ) function, which uses dictionary utility functions to return a parsed data packet in ASCII format along with any error status.


The script makes use of nested Python dictionaries to decompose the ASCII data values character-by-character by checking each against the key for each section of the dictionary. This script also implements classes which provide custom assertions that can be checked during the parsing of the data, providing specific error detection and reporting using the IC-CAP python treat_error() function to display the errors. You can open this script in any text editor and check out the source code to get an idea of how it works.  You can also modify this script to process other data formats including ones that provide other types of preambles and data resolution.


# Decode data packet
def get_ASCII_Data_Info(data, verbose=False):    
   Get the ASCII data value for parsing.     

   This function parsing data values returned from the instrument. Nested    
   data dictionaries are used for processing each element of the data value    
   including tags appended to the data value to provide measurement status.      

      data:       Data value to be parsed.        
      verbose:    Boolean value to provide more information to the calling                     
                  program. In this case IC-CAP will fill message text for treat_error function.    
      bOk:        Boolean value to report the success or failure of the function execution.        
      data:       Data value passed from calling progam.        
      stat:       Measuremetn status for data value.        
      chan:       Measurement channel for data value.        
      type:       Measurement data type ( current or voltage )        
      sign:       Polarity of the data value.        
      value:      Real value of the data.     

   bOk = True     
   mstat   = data[0]    
   mchan   = data[1]    
   mtype   = data[2]    
   msign   = data[3]     
   value   = data[4:16]     
   # for k,s in data:    
   stat   = getFromDict(dataDict, ['A', mstat])    
   chan   = getFromDict(dataDict, ['B', mchan])    
   type   = getFromDict(dataDict, ['C', mtype])    
   sign   = getFromDict(dataDict, ['D', msign])    
   svalue = float(value)     

   # 199.9999E+99 is a bad data point then set bad data points to 9999.9999    
      bOk = checkValue(svalue)                            
   except DataError as inst:        
      if verbose: treat_error(inst, printit=True)        
      bOk = False    
      bOk = True     

   # check the sign and multiply by -1    
   if msign == '-':        
      svalue *= -1.0 
   # check for errors and if verbose is true then display them        
      bOk = checkDataIsValid(stat)    
   except MeasError as inst:        
      if verbose: treat_error(inst, printit=True)    
   bOk = True          
   return bOk, { 'data':data, 'stat':stat, 'chan':chan, 'type':type, 'sign':sign,

## end of


Each data packet processed by the get_Measured_Data( ) function is appended to the global vvalue[ ] and ivalue[ ] arrays. When all data has been measured and parsed, the global array values are copied into IC-CAP data structures using ('M', 11) format with a 'for' loop with range set to the number of points in the sweep. Once the data are copied into the appropriate IC-CAP data array structures, the data can be plotted. We can also call other functions to export the data and write the results to both IC-CAP MDM and CSV file formats.


# Build iccap measured data values
dI = Transform(setupName + "/id")
gV = Transform(setupName + "/vg") 

# create data dictionaries for I-V data
dsI = {    
   ('M','11'): [ ivalue[i] for i in range(vg_points) ], 
dsV = {    
   ('M','11'): [ vvalue[i] for i in range(vg_points) ], 

# set the dataset in data dictionaries set_dataset(dI.get_fullname(), dsI)
if debug: print "Id :", dI set_dataset(gV.get_fullname(), dsV)
if debug: print "Vg :", gV  # export measured data to iccap mdm file
if bOk: bOk = export_Measured_Data() 

# write test results to CSV file
if bOk: bOk = write_Results(vg_points)            



Open the Plots Tab


Click the Display Plot button to view the data.




The plot results are shown in the new plot window.



Open the _close transform


This transform sets all bias to zero volts and disconnects the SMU outputs.

# close GPIB0 
if (vl): visaClear(vl) 
if (vl): visaCloseSession(vl) 

if debug: print "_close - exit bOk: ",bOk


NOTE: It is important to close the current VISA session when the transform finishes so that subsequent executions will not cause errors to occur when trying to perform the visaOpenSession command.




This implementation of the quasi-static measurement as a series of IC-CAP transforms demonstrates the flexibility and data handling capabilities of IC-CAP.  Using IC-CAP along with extending the Python modules like the and modules that I included as attachments to this article you can easily implement custom measurement and extraction routines. In a future article I'll show you how to implement additional transforms for extracting parameters along with other the custom Python code for performing parameter extraction on the Id vs. Vg sweep data of the p-channel MOSFET. 




[1] Vincent King Soon Wong, Hong Seng Ng, Florinna Sim., "Influence of Measurement System on Negative Bias Temperature Instability Characterization: Fast BTI vs Conventional BTI vs Fast Wafer Level Reliability". Internataional Journal of Electronics and Communication Engineering, Vol: 10, No: 12, 2016

[2] Wong, V. K. S., Ng, H. S., & Sim, P. C. (2015, December). "Impact of NBTI Recovery, Measurement System and Testing Time on NBTI Lifetime Estimation". 2015 International Conference on Advanced Manufacturing and Industrial Application. Atlantis Press.

[3] A.E. Islam, E. N. Kumar, H. Das1, S. Purawat, V. Maheta, H. Aono, E. Murakami, S. Mahapatra, and M.A. Alam "Theory and Practice of On-the-fly and Ultra-fast VT Measurements for NBTI Degradation: Challenges and Opportunities". Published 2007 in 2007 IEEE International Electron Devices Meeting.

[4] Keysight Technologies Application Note B1500-6, "Accurate NBTI Characterization Using Timing-on-the-fly Sampling Mode", Dec 2017.

[5] Keysight E5270B Programming Guide.


Related Links


rajsodhi, co-author


Many of you already know that Keysight's IC-CAP software provides an open flexible architecture that supports many industry standard and proprietary models. It also provides drivers for a range of popular test instruments required to make characterization measurements for extracting device model parameters and performing optimizations. But did you know that you can customize your IC-CAP environment to tackle your own measurement challenges?


This might be very welcome news if you are working with cutting-edge devices and need more than the capabilities already provided by IC-CAP. For many of these devices, skirting the limits of today’s technology often means that the measurement capabilities required are a moving target. And that can be a costly and time-consuming proposition if you have to buy and integrate new software tools every time you need new functionality. Your ability to automate these advanced measurements may even mean the difference between the success or failure of your project.


What if you need to implement your own behavioral model in Verilog-A along with custom parameter extraction routines? What if you want to create new measurement routines for controlling an arbitrary waveform generator? What if you want to add a time-domain measurement capability using an oscilloscope to capture your device’s fast pulse response? 


I faced such a scenario in my work at nSpace Labs, where I'm developing custom models for memristors and adding advanced measurement routines using the Keysight B1500A Semiconductor Device Analyzer. This analyzer is configured with multiple high-resolution SMUs (Source Measurement Units) and an integrated B1530A WGFMU (Waveform Generator/Fast Measurement Unit) to apply pulsed and arbitrary waveforms to the memristor’s electrodes, while simultaneously performing Fast-IV measurements. I needed to communicate with the WGFMU via GPIB using a Keysight provided software library and API for the WGFMU, something that is not currently supported by IC-CAP.


To take advantage of the WGFMU capabilities, I decided to use the Python programming language, included in all recent versions of IC-CAP, and PyVISA—a Python module that enables control of all kinds of measurement devices independent of the interface (e.g., GPIB, RS-232, USB or LAN). Utilizing IC-CAP's integrated Python environment, I was able to extend the software’s built-in measurement capabilities by integrating the third-party libraries and Python modules I needed for my application. This enabled me to programmatically generate the waveforms using Python, and then send the appropriate commands to the WGFMU for pulsing and simultaneously making Fast-IV measurements on my devices.


It took some effort, but I figured out how to install the WGFMU library and PyVISA package within a virtual Python environment for use with Keysight IC-CAP 2016. Now, I'd like to share with you the procedure for using some free Python packages and modules to extend the capabilities of IC-CAP. Using the WGFMU measurement routines that I have developed in Python, I am now able to extract and optimize parameters for my memristor model and have the ability to more accurately simulate the incremental conductance change of the memristor when a series of pulses are applied. Having more accurate models and the accompanying time-based measurement data provide me with a better understanding of the performance of the memristor-based circuits that I am designing. For you personally, extending the capabilities of IC-CAP means you can realize a significant return on your investment in your IC-CAP software, because you can now customize your environment to tackle whatever measurement challenge you might face.


Back to Basics

Before I delve into how to add PyVISA to a virtual Python environment, let’s discuss a few of the basics. First and foremost, the reason this is even possible is because the authors of IC-CAP developed a link between Python and IC-CAP back in 2013. The link provided in the module significantly expanded the flexibility and extensibility of the IC-CAP platform. The most common modules for engineering and scientific programming in Python are also currently included in the IC-CAP 2016 Python environment:

  1. Numpy: A fundamental package for numerical analysis including math functions, arrays and matrix operations.
  2. SciPy: A package for scientific and engineering computations.
  3. Matplotlib: A 2D plotting package that outputs plots in multiple popular graphics formats.
  4. PySide: A library that can be used for implementing custom graphical user interfaces using Qt.





There are a few things you’ll need to do prior to undertaking the installation of the PyVISA package:

  • Install IC-CAP_2016_01 or later, under Windows 7 in the default installation directory, which is typically C:\Keysight\ICCAP_2016_01.
  • Install the Keysight IO Libraries 17.x software and configure it to use the VISA library visa32.dll. This dynamic link library will be installed in the C:\Windows\system32directory.
  • Configure and test communication with your instruments using the Keysight Connection Expert software.                  

If you are new to IC-CAP software or Keysight IO Libraries, you’ll first want to get up to speed by checking out the links at the end of this article. If you are new to Python programming, I suggest you read the “About Python” section and check out the links at the end of this article before attempting this configuration.


Why Configure a Virtual Python Environment?

Sometimes, adding an incompatible module to a Python environment can cause it to become corrupted. Since IC-CAP ships with its own Python environment, you’ll have to be careful, or risk having to reinstall IC-CAP if problems arise. One way to avoid this problem is to use a virtual Python environment that can be implemented using two Python packages,"virtualenv" and “virtualenvwrapper-win,” which are designed to make it easy for users to create and manage completely isolated Python environments on the same computer.


By installing a separate virtual Python environment in your own user directory, you avoid breaking the IC-CAP installed environment, especially when potentially installing modules not compatible with the Python version shipped with your software. If you later upgrade to a newer version of IC-CAP, or if you need to reinstall the software for any reason, you’ll avoid having to reinstall all your third-party Python packages, assuming you have a backup of your home directory. 


The steps I’ve outlined for installing and configuring a virtual Python environment will allow you to install any third-party Python packages you want to use with IC-CAP. Key to this process is the installation of a standalone Python interpreter that includes PIP, a package manager needed to install the virtualenv and virtualenvwrapper-win. Having a stand-alone system Python environment is also great for developing experimental Python scripts and debugging in programming tools outside of the IC-CAP environment.



The following is a high-level overview of the procedure:

  • Install the Python 2.7 system environment
  • Install the virtualenv and virtualenvwrapper-win modules using PIP
  • Create your virtual Python environment for use with IC-CAP
  • Verify your new virtual Python environment
  • Configure the project directory for easy access to IC-CAPs Python interpreter
  • Install the PyVISA package using PIP in the virtual Python environment
  • Take PyVISA for a test drive using the virtual Python environment console
  • Write a macro within IC-CAP to activate the visual Python environment and use PyVISA


Step-By-Step Process for Installing the PyVISA Package


Step 1: Install the Python 2.7 system environment

Install the latest stable Python 2.7 release, which is currently version 2.7.13. Download Python 2.7.13 for Windows 64-bit from Python 2.7 is used because Python 3.x is not currently available for IC-CAP. Choose the default installation location, which is “C:\Python27”.



Add the Python 2.7 directories to the Windows PATH environment variable. This may be done through the Environment variables in Windows under the Advancedtab in computer Advanced system settings in Computer properties.


NOTE: The username in the command prompt is the name of the current user logged in to your Windows 7 machine. This generic name is a place holder for the commands listed below and represent the current user's home directory. The commands you will need to type are displayed in green throughout this article.


Check your Python installation.


C:/Users/usernamepython –V

Python 2.7.13


Step 2: Install the virtualenv and virtualenvwrapper-win modules using PIP

To install virtualenv type:

C:> pip install virtualenv


This should install virtualenv.exe into the C:\Python27\Scripts directory


To install virtualenvwrapper-win type:

C:> pip install virtualenvwrapper-win


This should install a set of useful batch '.bat' files that wrap the virtualenv functions and make creating and

managing virtual Python environments easier. These files should be installed in the C:\Python27\Scripts directory.


Check your virtualenv installation:

C:> virtualenv –-version



Step 3: Create your virtual Python environment for use with IC-CAP

Create the virtual environment using the virtualenvwrapper-win provided batch file mkvirtualenv and name it icenv.

Type the following at the command prompt:


C:\Users\usernamemkvirtualenv -p C:\Keysight\ICCAP_2016_01\tools\win32_64\python.exe icenv


NOTE: The -p option allows you to specify the IC-CAP Python 2.7.3 default environment to create our

virtual Python environment. Virtualenv creates the \Envs\icenv directory and copies the specified Python

interpreter and default modules to your \Users\username directory. 


Assuming everything succeeds with no errors or warnings, then we should get the following text:


Running virtualenv with interpreter C:\Keysight\ICCAP_2016_01\tools\win32_64\python.exe

New python executable in C:\Users\username\Envs\icenv\Scripts\python.exe

Installing setuptools, pip, wheel...done.


NOTE: The previous step creates the directory 'C:\Users\username\Envs\icenv' and copies the Python default

environment from C:\Keysight\ICCAP_2016_01\tools\win32_64. The contents of this '\Envs\icenv' directory

will now be the default Python configuration for IC-CAP 2016.


Use the cdvirtualenv batch file to easily change the directory to the icenv virtual Python environment we just created.


(icenv) C:\Users\username> cdvirtualenv


The command prompt should change to the following:


(icenv) C:\Users\username\Envs\icenv >


NOTE: This is the activated state for the icenv virtual Python environment. The 'activate.bat' file was called

during the mkvirtualenv command, which makes the following changes: 1) Prepending the virtual

environment's path to the Windows 7 system PATH environment variable so that it will be the first Python

interpreter found when searching for the python.exe. 2) Modifies the shell property to display the (icenv), which

denotes the virtual environment that is currently active. If no virtual environment is activated, the system  Python

is active by default.


Step 4: Verify your new virtual Python environment

Verify the activated Python 2.7 environment by checking the system path 'sys.path' and system prefix 'sys.prefix'


(icenv) C:\Users\username\Envs\icenv> python -V

Python 2.7.3


Start the interactive Python interpreter for the virtual environment.


(icenv) C:\Users\username\Envs\icenv> python


      You should see something like the following:

Python 2.7.3 (default, Feb 1 2013, 15:22:31) [MSC v.1700 64 bit (AMD64)] on win32

Type "help", "copyright", "credits" or "license" from more information.



Now, enter the following commands at the Python interactive prompt:

>>> import sys

>>> print sys.prefix


>>> print sys.path

['', C:\\Users\<username>\Envs\\icenv\\Scripts\\', 



>>> quit()

(icenv) C:\Users\username\Envs\icenv>


To exit the icenv virtual environment and return to the system Python environment, type the following at the command prompt:


(icenv) C:\Users\username\Envs\icenv> deactivate



Start the interactive Python interpreter for the system Python


C:\Users\username> python


You should see something like the following:

Python 2.7.13 (v2.7.3:a06454b1afa1, Dec 17 2016, 20:54:40) [MSC v.1500 64 bit (AMD64)] on win32

Type "help", "copyright", "credits" or "license" from more information.



Now, enter the following commands at the Python interactive prompt:


>>> import sys

>>> print sys.prefix


>>> print sys.path

['','C:\\WINDOWS\\SYSTEM32\\', 'C:\\Python27\\DLLs',

'C:\\Python27\\lib', 'C:\\Python27\\lib\\plat-win',  'C:\\Python27\\lib\\lib-tk',

'C:\\Python27', 'C:\\Python27\\lib\\site- packages']

>>> quit()



To reactivate the icenv virtual environment, use the virtualenvwrapper batch file 'workon.bat' by typing:


C:\Users\username\Envs\icenv> workon icenv

(icenv) C:\Users\username\Envs\icenv>


Step 5: Configure the project directory for easy access to IC-CAP’s Python interpreter

Here, we will set the project directory for the icenv virtual environment to IC-CAP's '\tools\win32_64' directory. This makes it easier to execute the built-in Python interpreter and import the IC-CAP included modules and Python tools.

Now you will no longer have to type the full path to access these tools.


NOTE: Anything you install using PIP while in the icenv virtual environment will still be installed

in the C:\Users\username\Envs\icenv.


Set the project directory to the ICCAP_2016_01 default Python path by typing the following:


(icenv) C:\Users\username\Envs\icenv> setprojectdir C:\Keysight\ICCAP_2016_01\tools\win32_64


"C:\Keysight\ICCAP_2016_01\tools\win32_64" is now the project directory for virtualenv



"C:\Keysight\ICCAP_2016_01\tools\win32_64" added to



Check that the project directory is set properly.


(icenv) C:\Users\username\Envs\icenv> cdproject

(icenv) C:\Keysight\ICCAP_2016_01\tools\wind32_64>


Return to the virtual environment directory.


(icenv) C:\Keysight\ICCAP_2016_01\tools\wind32_64> cdvirtualenv

(icenv) C:\Users\username\Envs\icenv>


Step 6: Install the PyVISA package using PIP in the virtual Python environment

Install PyVISA 1.8 using PIP.


(icenv) C:\Users\username\Envs\icenv> pip install pyvisa


Check that PyVISA was installed correctly to your (icenv) virtual python environment.


(icenv) C:\Users\username> cd C:\Users\username\Envs\icenv\lib\site-packages

(icenv) C:\Users\username\Envs\icenv\lib\site-packages > dir


The directory listing should now include the, visa.pyc, [pyvisa], and [PyVISA-1.8.dist-info] folders.


Step 7: Take PyVISA for a test drive using the virtual Python environment console 

You should have already installed the VISA drivers for your interface, either GPIB or PXI, and configured the system to use the software. Perform a "Scan for Instruments" to automatically add the instruments on your system to the VISA resources. Then, start an interactive Python shell and enter the following commands:


       (icenv) C:\Users\<username>>\Envs\icenv> python


       >>> import sys

       >>> import visa

       >>> print sys.path

        ['',  C:\\Users\<username>\Envs\\icenv\\Scripts\\',















      >>> rm = visa.ResourceManager()

      >>> print rm

      Resource Manager of Visa Library at C:\Windows\system32\visa32.dll


      >>> print rm.list_resources()



NOTE: An instrument was found on interface GPIB0 at address 16. Now quit the Python session and deactivate the virtual Python environment.


     >>> quit()

     (icenv) C:\Users\username>\Envs\icenv> deactivate




Step 8: Write a macro within IC-CAP to activate the icenv environment and use PyVISA

Open the project \examples\demo_features\5x3_PYTHON_PROGRAMMING\1_py_api_demo.


  1. Select the Macros tab.                                                                                                                                                           
  2. Click the “New... button to create a new macro.                                                                                                               
  3. Enter the name _init_pyvisa.                                                                                                                                                                                                                                                                                                                                                                                                        
  4. Select the Python (Local Namespace) radio button for the Macro Type.                                                                                                                                                                                                                                                                           
  5. Enter the following Python code in the editor window.                                                                                                                                                                         


  6. In Line #1 make sure to use the path of your home directory (where you created your icenv virtual Python environment), in this case, the 'C\Users\username\Envs\icenv\Scripts\' path.

  7. The script changes the current system PATH to prepend the (icenv) virtual Python environment to the existing PATH environment variable. This makes it the first location searched by the Windows OS when attempting to load a module (i.e., visa). The path entry 'C:\Users\marendall\Envs\icenv\lib\site-packages' will also be prepended to the system PATH environment variable and will be made available to IC-CAP's Python environment.

  8. Line #2 contains the execfile() function, which is called to launch the script. You can view the contents of this file in your favorite text editor or standalone Python IDE.

  9. Line #5 is where we import the module functionality into our IC-CAP Python environment.

  10. Line #10 gets the default VISA resource manager, which for our system is the visa32.dll installed with the Keysight IO Libraries in the C:\Windows\system32 directory.

  11. Line #13 calls the list_resources() function in the visa32 DLL library to return the current system resources

    previously configured with Keysight Connection Expert. This function returns a list of all the configured

    resources for the system as a Python List.

  12. In line #15 we print the result of the open_resource('GPIB0::16::INSTR') function, which should just return the     string 'GPIB Instrument at GPIB0::16::INSTR' if there are no errors.

  13. Line #17 closes the current communication session.


NOTE:  In practice you’ll want to code the statement in line #15 to be something like the following:

sl = rm.open_resource(r), where r is the resource string ('GPIB0::16::INSTR') and sl points to the object of the

opened VISA resource.


You can use call functions on this object by typing something like:

sl.write( cmd, termination='\r'), where cmd = "*RST" with the termination='r' being the carriage-return termination character to append to the end of the command string. This command will perform a GPIB reset on the instrument.


You can also use other available visa functions listed in the PyVISA docs, like sending: 

gpib_response = '\r') to read the response from the instrument's ouput buffer.


In a follow-on article I’ll show you how to create a PyVISA wrapper to simplify programming using these common

functions, but we’ll also be writing helper functions to check for instrument errors and display messages to the user.


NOTE: You should always close the VISA session when you are done with a resource. Not properly closing the session

will cause errors if you attempt to re-open the same resource later.                      


Step 9: Save and execute the macro within IC-CAP

Save the macro in the model file \Users\username\iccap\python\examples\1_py_api_demo.mdl or some other

suitable directory in your home directory.


  1. Click the Save button on the main toolbar, or select File->Save As from the main menu.        

  2. Select the macro _init_pyvisa from the Select Macro list.   

  3. Click the Execute button.                                                                                                                                                                                                                     

  4. Check the results of the script execution in the IC-CAP Status Window.                                                                             


The full content of the status window should read:















Resource Manager of Visa Library at C:\Windows\system32\visa32.dll


GPIBInstrument at GPIB0::16::INSTR


The Bottom Line

If you completed all of these steps successfully, you should now be able access the full capabilities of PyVISA from IC-CAP 2016. That means you can create transforms for instrument control and data acquisition over any supported interface.


Hopefully this information will be beneficial to those wanting to customize their IC-CAP 2016 installation to take full advantage of the many powerful Python packages available for download, or to write their own custom Python/PyVISA measurement routines. This method can also be used to install additional virtual Python environments for use with other Keysight products like WaferPro Express 2016. In a future blog post, I’ll outline the steps to do just that. In the meantime, for more information on IC-CAP or Keysight IO Libraries, go to and, respectively.



About Python

If you are new to Python programming, here’s some information to help you follow the steps in this blog.  

  • Python is an"interpreted" language, which means it generally executes commands typed by the user in an interactive command shell. This is convenient for testing program statements to learn the Python syntax. However, a more common means of writing a Python program is to create a Python script file with the '.py' extension. Say you created an example program and save it as '' To run the program, you simply type 'python' at the command shell prompt.
  • Python scripts, which are often called modules, can also be used for creating libraries of functionality, and are distributed along with program resources in packages.
  • Packages can be installed and combined with user-generated code to extend a program's functionality.  
  • PIP is the Python package installer that integrates with PyPI.orgthe Python Package Index. It is a repository of numerous free Python applications, utilities and libraries. PIP allows you to download and install packages from the package index without manually downloading, uncompressing and installing the package via the command 'python install'. PIP also checks for package dependencies and automatically downloads and installs those as well. 
  • An environment is a folder (directory) that contains everything a Python project (application) needs to run in an organized, isolated manner. When its initiated, it automatically comes with its own Python interpretera copy of the one used to create italongside its very own PIP. 



Keysight EEsof EDA’s Advanced Design System (ADS) software can help you overcome your signal and power integrity challenges. As the world’s leading electronic design automation for RF/MW and high-speed digital applications, ADS features a host of new technologies designed to improve productivity, including two EM software solutions specifically created to help signal and power integrity engineers improve high-speed link performance in PCB designs. 

Below are ten ways ADS can solve your most common SI/PI Design Challenges.


1. ADS provides speed and accuracy for your SI EM characterization

Don't be slowed down by increasing data rates. ADS provides two EM analysis solutions - SIPro and PIPro - that are specifically designed to handle high data rates, without sacrificing accuracy or speed. The limiting factor of 3D-EM technology for SI analysis is simply the scale and complexity of PCB designs. However, SIPro focuses on enabling SI EM analysis of high-speed links on large, complex high-speed PCBs, while PIPro is used for PI EM analysis of power distribution networks, including DC IR drop analysis, AC PDN impedance analysis, and power plane resonance analysis. 


Figure 1. SIPro delivers results approaching the accuracy of full-wave 3D-EM solutions, but in a fraction of the time.


2. ADS simplifies the use of S-parameter files for your parts

Imagine you’ve just downloaded an S-parameter file for a part you are considering; in this case, a high-speed connector for a backplane. It has a large number of ports on it, so you want to first inspect the quality of the data and then use it in your simulation. How do you wire it up? Which ports are paired?

Traditionally, the answer might be to open up the data in a text editor. However, with ADS’s S-Parameter Checker, designers can now easily view the contents of any S-parameter file without having to setup an S-parameter test bench simulation. This allows them to directly plot the individual relations they wish to see, and shows them the port names against each pin. It also tells designers if the file is passive or reciprocal, as well as the number of data points in the file and the frequency range it covers. Designers can even use S-Parameter Checker to rename, re-order and reduce the number of ports, which enables them to save a new, more usable S-parameter file (Figure 2).



Figure 2. The S-Parameter Checker allows design engineers to easily rename, re-order and reduce the number of ports.


3. ADS provides access to industry-leading channel simulator technology

ADS's channel simulator technology sprung forth in 2009 when transient simulation (SPICE) couldn’t address the measurement of margin-to-mask for really low bit-error-rates (BER), as demanded by high-speed link designs.

Dr. Fangyi Rao's 2006 patent to correct for passivity, while ensuring causality, ensured ADS to be regarded as the msot accurate solution for handling cascades of S-parameter models combined with circuit models in one schematic. The pace of innovation continues today with ADS's Channel Simulator still the industry-standard model. Additionally, the Channel Simulator now supports IBIS package (.pkg) entries directly and more extensively before.


Figure 3. With ADS, designers can mix-and-match models from IBIS, IBIS-AMI, SPICE, and generic built-in models.


4. ADS stays ahead of technology waves (such as PAM-4)

Market pressures on IP routers set an expectation to do more at a lower cost per bit. However, to go faster and provide a single 100-Gbps electrical lane across the distance of a typical backpane is beyond present day technology. 

The solution lies in Pulsed-Amplitude Modulation (PAM) for high speed serial links. PAM represents a revolutionary step in the industry, but comes with its own unique set of challenges as well. For example, we can transmit a PAM-4 symbol at 28 Gbaud and deliver 56 Gbps at the other end, but the IC's use more power and the signal itself has a reduced Signal-to-Noise ratio (SNR). 

Whether you are challenged with managing complexity while reducing production cost, or researching how to go further and faster on low-loss materials and fabrication processes,


 Figure 4. ADS supports PAM-4 simulations, which offers a viable alternative to NRZ. 



5. ADS accelerates DDR4 simulation methodologies

In simulations, how do you check compliance against the mask? Keysight EDA offers a novel DDR Bus simulator specifically designed to accomplish this task. It is a bit-by-bit channel simulator for parallel buses. It characterizes all transmitter paths at once, and calculates the BER contours for each eye at the receive side, together with the measurements for margin to mask. 

The simulator is unique in that it correctly handles the asymmetric rise and falling edges found with single-ended signals.The Tx and Rx models can be used to drive IBIS models, or mixed with SPICE models. The speed of the simulator allows it to be used in place to transient simulation for many pre-layout tasks, where the designer wants to sweep multiple parameters, or investigate performance movements. Together with batch simulation, it is a powerful tool for pre-layout design exploration, as well as post-layout verification for compliance.


Figure 5. Keysight EEsof EDA's DDR Bus simulator is a bit-by-bit channel simulator for parallel buses.


6. ADS puts power in the hands of designers

Power Integrity has become an ever-increasing challenge in modern day high-speed systems, driven by three main forces: higher device integration, lower IC supply voltages, and smaller real estate on the PCB. These modern challenges have forced engineers out of their notebooks and into true PI-DC simulators in order to take into account the real physical layout of the power delivery network (PDN). 

With ADS and the new PIPro suite of EM simulators, designers receive visual feedback in just second on exactly what the voltage distribution looks like for the selected power and ground nets. ADS also allows designers to check the current flow through individual vias and the voltage and current at specific locations like individual pins on the sinks and voltage regulator module (VRM). This information is easily reported in a sortable table. Vias that carry too much current can be highlighted in the layout for easy identification (Figure 6).


Figure 6. Vias carrying too much current can be highlighted in the layout for easy identification.


7. ADS enables flat PDN impedance responses

Once the initial pre-layout design has been created, the first-pass of the PCB layout can be imported into ADS 2016 for analysis using PIPro EM technology. PIPro’s net-driven user interface allows designers to quickly select the power and ground nets for the PDN network they want to simulate, choose simulation models for each of the components (e.g., decaps, EMI filters, inductors, and resistors, etc.), and setup the PI-AC simulator to compute the PDN impedance of the distributed layout with components in place. 

Since the PI-AC simulator has EM technology designed specifically for this purpose, a very accurate result is returned in minutes, not days. Designers can then use ADS 2016’s field visualization, PDN impedance and S-parameter plotting to determine if there are problems with the current PDN design, and to check coupling from one capacitor to the next. With just one click, a schematic representation is generated to transfer the EM-characterized model, together with circuit models of the components.  This back-annotation to an ADS Schematic enables  one smooth cohesive workflow. Designers can then apply their behavioral VRM model, and further tune the decaps for final verification/optimization.


Figure 7. Increasing the decoupling capacitance while increasing ESR improves impedance response flatness.


8. ADS enables electro-thermal simulation

As power delivery networks are forced into tighter PCB real-estate, the power plane becomes far from idealized.  Usually the once perfect plane is perforated heavily with clearance holes from stitching vias, and it can be a struggle for the layout engineer to get the required current up into the package of the device that requires it, without passing through narrow traces of metal.  Calculating an accurate IR-Drop is important for the PI designer, but also knowing the absolute temperature that the PDN traces, vias and chip die will reach, is invaluable information.   High temperatures can cause reliability issues; as the temperature cycles from on/off states can cause the via barrels to weaken and crack over time.

It is not intuitive to the designer whether a via is undersized for the current that is passing through it.  The temperature rise is very dependent on the width of the traces attached to it.  Secondly, resistance of a trace increases with temperature, requiring simulation analyses to determine the final steady state condition.  For every 10 degC change in temperature we see a 4% change in resistance of a trace. These observations point to a need to simulate the PDN design with a DC IR Drop electro-thermal solution.    

ADS provides a fully-automated integrated Electrical-Thermal-Electrical iterative simulation.  Users receive the most accurate representation of DC IR Drop results by taking into account local resistivity changes due to heating. The additional Thermal-only simulation, gives the user the ability to perform thermal floor planning.

With ADS you can easily copy existing DC IR Drop simulation setup to new Electro-Thermal simulation and visualize a list the temperature of planes, pins and vias.


Figure 8. DC IR Drop Electro-Thermal analysis - visualization of temperature.


9. ADS has an interconnect toolbox (Via Designer and CILD)

The signal integrity design challenge is not just to successfully recover the transmitter signal at the receiver, but to understand what is controlling the performance.  What are the significant margin eaters and which ones can I optimize?

Typical connections between a transmitter and a receiver include some section of application specific custom PCB routing.  ADS has a signal integrity tool box to help explore the design trade-offs and deal with the complex interaction between stack-up, transmission line losses, and via topology.

Designing the PCB interconnect starts with some sort of PCB stack-up definition in order to start evaluating the different types of possible transmission line topologies.  Once the transmission lines are optimized for impedance and losses, then one needs to look at via performance to transition between layers. Anyone of these steps has cost and performance trade-offs that can impact the other, resulting in a complex inter-relationship to determine which feature is the real margin eater:  Layer Count, Line Z, Via Backdrills, Material, Layout Density, etc.

ADS provides an Interconnect Tool Box that includes Substrate Editor, Controlled Impedance Line Designer, and Via Designer to simplify the pre-layout PCB interconnect investigation.

Figure 9. This type of pre-layout investigation enables an engineer to understand what is controlling the design margins and make informed cost vs. performance decisions.


10. ADS embodies the Keysight philosophy:

Hardware + Software + People = Insights

With Keysight's greater software-centric solutions focus, Keysight EEsof EDA plays a leading role in virtual compliance testing. Through Compliance Test Benches in ADS, designers can now take ADS simulated waveforms and test them against the same gold suite of compliance tests used on the bench with final verification hardware to attain the utmost confidence in a designs's compliance.

Further bolstering these capabilities in ADS is the support Keysight EDA offers its customers. That support includes a world-wide technical support presence, expert Application Engineers and consultative Field Sales Engineers. This support, together with Keysight’s hardware and software solutions and technical expertise gives customers greater insight and in turn, greater chance of success.



Keysight ADS further cements its leading position in electronic design software with continued advances for circuit simulation, layout and layout verification, silicon RFIC, and just as critically, signal and power integrity.


Welcome to Tim’s Blackboard! This is the place to find discussions on interesting topics related to signal integrity and power integrity. Find other cool posts here!


Last time on Tim’s Blackboard, we talked about linear Feed-Forward Equalization (FFE). This week, we will discuss the nonlinear Decision Feedback Equalization (DFE).


All the ADS content shown is in the attached workspace. Make sure you download the attached workspace and apply for a free trial to apply DFE to your own channel!



When I first learned about decision feedback equalization, one of the bullet points is, “it is a nonlinear equalizer”, but I never knew why. Today, I will answer the question:

What makes DFE a nonlinear equalizer? 

Decision Feedback Equalization Technique

Shown in Fig. 1, decision feedback equalizer (DFE) can open a closed eye. Nonetheless, the signature of an opened DFE eye is different than other equalizations. There are kinks in the eye diagram. To examine the eye diagram a little closer, we apply single pulse analysis to look at the blink of an eye.


Fig. 1: Keysight ADS channel simulation demonstrating Decision Feedback Equalization (DFE) with different number of taps. DFE exhibits kinks in the eye.


Just like the eye diagram, we expect the single pulse response after decision feedback equalization to also have kinks. Sure enough, in Fig. 2, we see the kinks in the equalized single pulse response.


Fig. 2: Equalized single pulse response shows how DFE corrects post-cursor ISI on a single pulse that has all 0’s but a single 1. DFE inserts negative amplitudes after the received “1” pulse to better detect the next 0.


Taking a closer look, one observes that since the single pulse has all 0’s but a single 1 in Fig. 2, as soon as DFE algorithm sees a 1, it tries to reduce inter-symbol interference (ISI) by adding negative amplitudes so that the following low voltage is lower, allowing better detection of the next 0.


By the same token, when we send a single pulse that has all 1’s but a single 0, we should expect that as soon as the algorithm sees a 0, it tries to reduce ISI by adding positive amplitudes, allowing better detection of the next 1.


Fig. 3: Equalized single pulse response shows how DFE corrects post-cursor ISI on a single pulse that has all 1’s but a single 0. DFE inserts positive amplitudes after the received “0” pulse to better detect the next 1.


Result shown in Fig. 3 is consistent with our expectation. DFE algorithm is reducing ISI based on the detected data (symbol).


Fig. 4: Comparison between received waveform and equalized waveform shows how DFE acts on the received waveform.  


By comparing the received waveform and waveform after DFE, as seen in Fig. 4, we can further see the action of DFE algorithm, but the question remains:

What makes DFE a nonlinear equalizer?

Symbol Detection and Decision: A Nonlinear Filter

At the arrival of received data (symbols), DFE algorithm detects and makes a decision. Assuming the decision is correct, proper tap values are chosen and feedback to the originally received data.   


Fig. 5: An example of a symbol detector. Because the output does not scale linearly with the input, a symbol detector is nonlinear.  


Shown in Fig. 5 is a symbol detection processing block. As the input doubles from 0.6 V to 1.2 V, the output does not double. Consequently, symbol detection is nonlinear. In turn, decision feedback equalization is also nonlinear.

But how do we make sure the detection is correct?

Fig. 6 is an illustration of DFE block diagram. The received symbol first undergo feedforward equalization so that the symbol detector can make the correct decisions. After the symbol detector makes a decision, the result goes through a feedback filter to be combined with the previously detected symbol.  


Fig. 6: Decision Feedback Equalizer (DFE) block diagram. A feedforward filter is at the front end of DFE to help the symbol detector make a correct decision. Each decision then goes through feedback filter to be combined with previous symbol.


Because the input to the feedback filter consists of the sequence of decisions from previously detected symbols, which it uses to remove the portion of the ISI caused by those symbols, DFE only removes post-cursor ISI. Moreover, since DFE assumes that past symbol decisions are correct. Incorrect decisions from the symbol detector corrupt the filtering of the feedback loop. As a result, the inclusion of the feedforward filter on the front end is crucial in minimizing the probability of error [1].


Realization of Decision Feedback Equalization

Given the basic algorithm of DFE, I decided to design my own DFE, see Fig. 7.


Fig. 7: Demonstration of a homemade DFE in Keysight ADS. See attached workspace for detail.


Knowing the input sequence is going to be a single “1” pulse, i.e. all 0's but and single 1, I first changed the feedback filter coefficients, V_tap1 and V_tap2, until the post-cursor ISI is reduced enough. Then, I adjusted the delay of the two taps so the corrections take place at the right time. When all was said and done, I had created a homemade 2-tap DFE. Fig. 8 shows the equalized single pulse response.


Fig. 8: Applying homemade 2-tap DFE to a single pulse.


In the process of creating a homemade DFE, I learned that DFE algorithm is not trivial. It requires many moving pieces to align. Besides the correct symbol detection, the timing and feedback filter coefficients (tap values) also need to be appropriately selected for different channels.


Fig. 9: DFE algorithm is readily available in Keysight ADS channel simulation. There are several adaptive algorithms to choose from.


Good news! To help expedite the simulating and testing process, DFE algorithms are implemented and readily available in ADS. ADS helps you test the amount of stress your channel can handle with DFE and adaptive DFE enabled, see Fig. 9. 


Summary of Equalizations

After today, we have talked about all three equalizations,

·        Continuous Time Linear Equalization (CTLE),

·        Feed Forward Equalization (FFE),

·        Decision Feedback Equalization (DFE).


Below is a summary of the equalizations.

Table 1: Summary of Equalization Techniques 


Each of the equalizations has its own personality. While CTLE is sitting in the analog world, operating in the frequency domain, in the digital realm, FFE and DFE are working comfortably in the time domain.


Of course, each personality has its strength and weakness, and so does each equalization. In the near future, I will examine the pros and cons of equalization techniques. Make sure you bookmark the blog and check back regularly.    


For the upcoming post, I will take a step back and ask the question:

What is Signal Integrity? 

Until next time, make sure you download the attached workspace and apply for a free trial to apply DFE to your own channel!



[1]       S. H. Hall, Advanced signal integrity for high-speed digital designs. 2009.