# EEsof EDA

6 Posts authored by: michea

# Computing the Derivative of I-V Data Using Python in IC-CAP

Posted by michea Dec 17, 2018
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.
(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)]

i=0
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 ic_math.py 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 ic_math.py:

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.

#############################################################################################

#  Test_npdiff.py
#  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

gm.insert(0,1E-10)

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

# 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

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.

### Conclusion

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.

# Using Python to Access IC-CAP Objects and Data Structures

Posted by michea Oct 31, 2018

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:

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

`# query setup variablesvg_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.

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.

### Prerequisites

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.

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

python\py_api_demo_5270_v.2.0.mdl.

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               Commentdrain           1                   Drain SMU Channelgate            2                   Gate SMU Channelsource          3                   Source SMU Channelsubstrate       4                   Substrate SMU Channelinterface       GPIB0:17::INSTR     VISA instrument resourcevisaResponse                        VISA response stringerror_status    32,48,128           Status bytes to check for errorserror_command   ERR?,EMG?,ERRX?     Commands to process error eventshold_bias       0.0                 Bias applied during hold time periodhold            0.5                 Hold time before first measurement, in secondsdelay           0.1                 Delay timestep_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 hold=float(SVar("hold").get_val()) delay=float(SVar("delay").get_val()) step_delay=float(SVar("step_delay").get_val())...``

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.

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

``Variable Name   Descriptionvgstart         Gate sweep start voltagevgstop          Gate sweep stop voltagevgstep          Gate step sizevgpts           Gate pointsigcompl         Gate current compliancevdvalue         Drain voltage (constant)idcompl         Drain current compliancevsvalue         Source voltage (constant)iscompl         Source current compliancevbvalue         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 setupif debug: print "fullname:",s.get_fullname()if debug: print "name:", s.nameif 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 orderif 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 typeif debug:   print "child objects of Setup:"   for objchildtype in s.childtypetuple:      print objchildtype      for objchild in s.get_child_objects(objchildtype):         print " ", objchild.name         print`

Child object query response:

child objects of Setup:

Input

vd

vg

vs

vb

Output

id

Transform

_run

_init

_setup_iv

_meas_iv

_close

_calc_vth

SVar

fileName_PATH

fileName_PATH

drain

gate

source

substrate

interface

visaResponse

error_status

error_command

hold_bias

hold

delay

step_delay

MDM_FILE_PATH

MDM_FILE_NAME

Plot

ThresholdVoltageIdsvsVgs

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 inputsweepName = 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 TableVarif debug: print "fullname :", igcompl.get_fullname()if debug: print "name :", igcompl.nameif debug: print "mytype :", igcompl.mytypeif debug: print "value :", igcompl.get_val()if debug: print# query TableVarif debug: print "fullname :", vgstart.get_fullname()if debug: print "name :", vgstart.nameif debug: print "mytype :", vgstart.mytypeif 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 valuesvg_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 tablepolarity = 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 inputconstName = 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 TableVarif debug: print "fullname :", idcompl.get_fullname()if debug: print "name :", idcompl.nameif debug: print "mytype :", idcompl.mytypeif debug: print "value :", idcompl.get_val()if debug: print# query TableVarif debug: print "fullname :", vdvalue.get_fullname()if debug: print "name :", vdvalue.nameif debug: print "mytype :", vdvalue.mytypeif 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 valuesid_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.

### Conclusion

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.

### References

IC-CAP 2018 user-manual and documentation

Example model file "py_api_demo.mdl" under Macros

# Using Python for Implementing Parameter Extraction in IC-CAP

Posted by michea Jul 13, 2018

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 pyvisawrapper.py and dataparser.py 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.

## Prerequisites

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.

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 extract_vth_cc.py also attached below. You should copy the extract_vth_cc.py 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 valuesvvalue = []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 vthif 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 * ################################################################# # _calc_vth.py # # 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.

### extract_vth_cc.py source code listing

``import numpy as npfrom 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.       Args:           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.       Kwargs:       Returns:       instance of class       Raises:       """       self.id = id  # a list []       self.vg = 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.          Args:             self:      This instance of the extract_vth_cc class.          Kwargs:          Returns:             Vth:       Interpolated gate threshold voltage at icon.          Raises:          """                # initialize local variables          vth =1E-30        type = 1       # nmos                 vg = self.vg        id = self.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.

## Conclusion

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.

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

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

Posted by michea Jun 26, 2018

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

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.

## Prerequisites

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 pyvisawrapper.py and dataparser.py scripts attached to this post.The pyvisawrapper.py 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 dataparser.py 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 pyvisawrapper.py and dataparser.py 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

>>>

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:

``[''\,C:\\Users\\username\\Envs\\icenv2018\\Scripts\\python27.zip', 'C:\\Users\\username\\Envs\\icenv2018\\DLLs', 'C:\\Users\\username\\Envs\\icenv2018\\lib', 'C:\\Users\\username\\Envs\\icenv2018\\lib\plat-win', 'C:\\Users\\username\\Envs\\icenv2018\\lib-tk', 'C:\\Users\\username\\Envs\\icenv2018\\Scripts', 'C:\\Keysight\\ICCAP_2018\\tools\\win32_64\\Lib', 'C:\\Keysight\\ICCAP_2018\\tools\\win32_64\\DLLs', 'C:\\Keysight\\ICCAP_2018\\tools\\win32_64\\lib-tk', 'C:\\Users\\username\\Envs\\icenv2018\\lib\\site-packages', 'C:\\Keysight\\ICCAP_2018\\tools\\win32_64']``

To exit the virtual Python interpreter type:

``>>> quit()``

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               Commentdebug           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.

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

``Name            Value               Commentdrain           1                   Drain SMU Channelgate            2                   Gate SMU Channelsource          3                   Source SMU Channelsubstrate       4                   Substrate SMU Channelinterface       GPIB0:17::INSTR     VISA instrument visaResponse                        VISA response string error_status    32,48,128           Error codes error_command   ERR?,EMG?,ERRX?     Command to process errorshold_bias       0.0                 Bias applied during hold time periodhold            0.5                 Hold time before first measurement, in secondsdelay           0.1                 Delay timestep_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 'activate_this.py' script, which will activate the virtual python environment. Modify the path defined for the 'activate_this' variable, to the correct setting for the environment.

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 'activate_this.py' script should be modified to be correct for the current user environment)

``################################################################# # _run.py # # Main execute file for IdVg measurement #  ################################################################# activate_this = "/Users/<USERNAME>/Envs/icenv2018/Scripts/activate_this.py" execfile(activate_this, dict(__file__=activate_this))  # imports from iccap import icfuncs as f, SVar, MVar, get_dataset, set_dataset, Setup, TableVarfrom 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.

``################################################################# # _init.py # # 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 hold=float(SVar("hold").get_val()) delay=float(SVar("delay").get_val()) step_delay=float(SVar("step_delay").get_val())  vg_start=float(SVar("vgstart").get_val()) vg_stop=float(SVar("vgstop").get_val()) vg_step=float(SVar("vgstep").get_val()) ig_compl=float(SVar("igcompl").get_val())  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])  vd_value=float(SVar("vdvalue").get_val()) id_compl=float(SVar("idcompl").get_val()) if polarity == "PMOS":        vd_value *= -1        id_compl *= -1 if debug: print "vd value = {}".format(vd_value)  vs_value=float(SVar("vsvalue").get_val()) is_compl=float(SVar("iscompl").get_val())  vb_value=float(SVar("vbvalue").get_val()) ib_compl=float(SVar("ibcompl").get_val()) 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        vvalue.append(value)         # 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 valuesvvalue = [] ivalue = []  ...`

The get_Measured_Data( ) function retrieves the data from the instrument data buffer and uses the dataparser.py  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)

``ABCDDDDDDDDDDDDD     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 dataparser.py 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.          Args:               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.        Kwargs:         Returns:               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.         Raises:               DataError               MeasError         """         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        try:               bOk = checkValue(svalue)                                except DataError as inst:               if verbose: treat_error(inst, printit=True)               bOk = False        else:               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            try:               bOk = checkDataIsValid(stat)        except MeasError as inst:               if verbose: treat_error(inst, printit=True)        else:            bOk = True                 return bOk, { 'data':data, 'stat':stat, 'chan':chan, 'type':type, 'sign':sign,                  'value':svalue }  ## end of dataparser.py``

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.

## Conclusion

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 pyvisawrapper.py and dataparser.py 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.

## References

[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.

rajsodhi, co-author

# Extending the Power of IC-CAP Software with Python—PyVISA Instrument Control

Posted by michea May 10, 2018

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 iccap.py 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.

Prerequisites

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.

Overview

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.org. 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.

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.

C:> virtualenv –-version

15.1.0

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:

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

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

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.

The command prompt should change to the following:

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'

Python 2.7.3

Start the interactive Python interpreter for the virtual environment.

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

>>>

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

>>> import sys

>>> print sys.prefix

>>> print sys.path

'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\Lib',
'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\DLLs',
'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\lib-tk',
'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64']

>>> quit()

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

Start the interactive Python interpreter for the system 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

>>>

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

>>> import sys

>>> print sys.prefix

C:\Python27

>>> print sys.path

['','C:\\WINDOWS\\SYSTEM32\\python27.zip', '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:

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

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

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

Check that the project directory is set properly.

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

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

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

Install PyVISA 1.8 using PIP.

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

The directory listing should now include the visa.py, 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:

>>> import sys

>>> import visa

>>> print sys.path

'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\Lib',

'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\Lib',

'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\DLLs',

'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\lib-tk',

'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64']

>>> rm = visa.ResourceManager()

>>> print rm

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

>>> print rm.list_resources()

(u'GPIB0::16::INSTR',)

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

>>> quit()

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 activate_this.py 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 activate_this.py 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 visa.py 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 = sl.read(termination= '\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:

'C:\\Keysight\\ICCAP_2016_01\\bin\\python27.zip',

'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\DLLs',

'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\lib',

'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\lib\\plat-win',

'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\lib\\lib-tk',

'C:\\Keysight\\ICCAP_2016_01\\bin',

'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64',

'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\lib\\site-packages',

'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\Lib',

'C:\\Keysight\\ICCAP_2016_01\\iccap\\lib\\python',

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

(u'GPIB0::16::INSTR')

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 www.keysight.com/find/eesof-iccap and www.keysight.com/find/IOlibraries, respectively.

References:

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 'example.py.' To run the program, you simply type 'python example.py' 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 setup.py 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.

# Using a Python VISA API within IC-CAP for Instrument Control

Posted by michea Dec 13, 2017

Have you ever found yourself in a situation where the characterization software did not support the hardware you were trying to use?  Your tests may be complex, where the measurement conditions of your next data point depend on previous measurement results.  I have found that using Python integrated with the IC-CAP software leads to a powerful and flexible solution that is supported by an open community. To help with the test development, I have found it useful to create a software layer that handles error checking and low-level function calls. In what follows, I'll describe the use of a "PyVISAWrapper" that can simplify your test suite development.

## My Motivation

I found myself in a tricky situation when I started using IC-CAP to characterize and model my memristor devices. I needed to perform a quasi-static measurement to precisely control the ramp rate of the voltage sweep by adding a hold time and source delay time, and to control the delay between measurements. However, in some tests, the measurement conditions for the next measurement in IC-CAP depend on previously measured values. Because of that, IC-CAP’s built-in, general-purpose instrument drivers didn’t support my need to perform a quasi-static measurement.

The solution I found to my dilemma was to write Python code in IC-CAP to control my instruments. The process builds on my previous post, Extending the Power of IC-CAP with Python - PyVISA Instrument Control. Using this process, you too can write Python code to control your instruments and create highly flexible measurement routines.

The PyVISA library turned out to be a great way for me to meet these goals, plus it enables portability across several control interfaces (e.g., GPIB and LAN). And, it was tempting for me to dive right in and start writing instrument specific commands using the default GPIB interface. Instead though, I took a step back and thought about how to make my Python code more general. To that end, I decided to create a wrapper for PyVISA’s low-level library functions that would allow me to send the most common VISA calls to control an instrument (e.g., open(), write(), read(), query(), etc.).

## PyVISA Wrapper Utility

You might ask, “Why employ a wrapper utility to use the PyVISA library for instrument control?” It’s a fair question and the answer is pretty straightforward. The wrapper utility may be considered a high-level API for controlling instruments. With carefully constructed API function calls, an engineer can more easily create a suite of tests leveraging smarter I/O function calls, including writing instrument commands, querying responses, checking for command complete events, parsing the measurement data, checking for errors, and providing meaningful troubleshooting information.

What follows are details on how you can use an open source PyVISA wrapper utility I wrote called pyvisawrapper.py to simplify writing custom Python code to control almost any instrument. The example code provides the same functionality as the _init_pyvisa macro presented in my last article, but it also adds some useful features described above. The pyvisawrapper API facilitates communication with any instrument that uses TCP/IP or USB, in addition to the standard GPIB. Using the E5270B analyzer as an example instrument, I’ll revisit the _init_pyvisa macro and illustrate the advantages of using the pyvisawrapper functions over low-level function calls.

## Prerequisites

Since for this process, we’ll leverage the IC-CAP Python library, as well as the PyVISA library, using import statements, it’s important to make sure you have your Python environment configured correctly. Here’s a few of the basic prerequisites:

1. Install IC-CAP_2016_01 or later, under Windows 7 in the default installation directory, which is typically C:\Keysight\ICCAP_2016_01.
2. 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\system32 directory.
3. Configure and test communication with your instruments using the Keysight Connection Expert software.
4. Before attempting this example, install and configure a virtual Python environment and install the PyVISA library presented in my aforementioned article, Extending the Power of IC-CAP with Python - PyVISA Instrument Control. Key to this process is the installation of a standalone Python interpreter, which will be great for developing experimental Python scripts and debugging in programming tools for making future modifications to your custom code, like pyvisawrapper itself, outside of the IC-CAP environment.
5. Download and copy the pyvisawrapper.py script to your user directory. I personally like to create a sub-directory under my account C:/Users/username/iccap/python to store my own custom python code.
6. Set the ICCAP_USER_PYTHON_PATH environment variable in your Windows 7 advanced systems settings to add this directory to the search path. This will allow IC-CAP to find your Python scripts.
7. You will need some instruments that can be remotely controlled via GPIB or LAN. Any instrument will do.

## Overview

The following is a high-level overview of the code we are going to implement:

• Test your new virtual Python environment installed as part of the prerequisites.
• Create a new IC-CAP macro named _pyvisa_run.
• Activate the virtual Python environment from your script.
• Import the iccap.py and visa.py modules from the IC-CAP and virtual Python 2.7 environments.
• Import the pyvisawrapper.py to use the high-level API functions in your macro.
• Use the visaOpenSession() function to access the PyVISA ResourceManager
and perform the VISA open() on the Keysight E5270B resource via the GPIB interface.
• Use the visaCloseSession() to close the resource before exiting the script.
• Use the visaQuery() function to send the *IDN? command to the E5270B and read its response.
• Use the visaClear() function to perform a GPIB device clear and leave the GPIB interface in a initialized state.

## Step-By-Step Process for Using pyvisawrapper

### Step 1. 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.' To do this, open a Windows command shell and perform the following steps:

Activate the (icenv) virtual environment

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

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

Change the directory to virtual env (icenv)

``C:\Users\username> cdvirtualenv``

Check the Python interpreter version for (icenv)

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

The version should return:

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

>>>

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:

``[''\,C:\\Users\\username\\Envs\\icenv\\Scripts\\python27.zip', 'C:\\Users\\username\\Envs\\icenv\\DLLs', 'C:\\Users\\username\\Envs\\icenv\\lib', 'C:\\Users\\username\\Envs\\icenv\\lib\plat-win', 'C:\\Users\\username\\Envs\\icenv\\lib-tk', 'C:\\Users\\username\\Envs\\icenv\\Scripts', 'C:\\Users\\username\\Envs\\icenv\\DLLs', 'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\Lib', 'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64\\DLLs', 'C:\\Keysight\\ICCAP_2016_\\tools\\win32_64\\lib-tk', 'C:\\Users\\username\\Envs\\icenv\\lib\\site-packages', 'C:\\Users\\username\\Envs\\icenv\\DLLs', 'C:\\Keysight\\ICCAP_2016_01\\tools\\win32_64']``

To exit the virtual Python interpreter type:

``>>> quit()``

Leave the virtual environment by typing:

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

If you made it this far then your icenv virtual Python environment is setup and ready to be used from IC-CAP.

### Step 2. Start IC-CAP, then create a new model file and add a new macro

Go the the main IC-CAP menu and select File, then New. Name your model file ‘pypvisa_example’.

Select the Macros tab and then click New… to create a new macro.

Enter _pyvisa_run for the name of the new macro.

### Step 3. Add variables to the IC-CAP Model Variable table

Select Model Variables and enter the following variables in the Model Variable table:

We'll describe each of these variables as follows.

• The `interface`  variable should be set to the VISA resource string as listed under the `My Instruments` panel in Keysight Connection Expert or after performing a `scan for instruments.` This is also the string returned from PyVISA after sending the `list_resources()` function to the default resource manager.
• The `visaResponse` variable holds the response string from the latest command sent to the instrument.
• The `error_status `is a list of values to look for in the status byte register that would indicate an error.  This information should be available in the instrument's programming guide. In the case of the E5270B analyzer, the decimal value for the error status bit is `32.` The error status could potentially be returned as `48` if the `Set Ready` bit is also active, or `128` if an emergency error is reported after the last command sent.
• The `error_command` is the list of instrument specific commands necessary for returning the error `code` and the error `message `string from the instrument's message buffer. In our E5270B example, the error commands are "`ERR?"` and "`EMG?"`, with "`EMG?"` being the command that is sent after processing the list of codes returned from the "`ERR?"` query. This command will return the error message string for the associated error code passed as a parameter when performing the query.  "`ERR?"` is the query that works with the B1500A to do extended error reporting.

The pyvisawrapper code performs all of this detailed error checking for you!  This is incredibly handy.  Simply setting the debug variable to 1 in the Model Variable table will enable debug prints in the macro and pyvisawrapper, and output verbose debug text that is displayed in the IC-CAP Output window. This information will aid you in troubleshooting your Python code.

### Step 4. Activate the virtual Python environment

``activate_this = "/Users/username/Envs/icenv/Scripts/activate_this.py"execfile(activate_this, dict(__file__=activate_this))``

The "username" in the first line is the name of the current user logged into your Windows 7 machine. This generic name is a place holder for the commands listed below and represent the current user's home directory.

### Step 5. Import the iccap.py functions and pyvisawrapper.py

``from iccap import icfuncs as f, MVar from pyvisawrapper import *``

Now we have access to all the functions in pyvisawrapper.py and iccap.py.

### Step 6. Add some local variables and variables that access the Model Variable table

``bOk = True                                          # return status of the called function cmd = ""                                            # command string instr = MVar("interface").get_val()                 # visa resource to use from model var table rsp = MVar("visaResponse")                          # visa response string from read or query stat = MVar("error_status").get_val().split(",")    # status byte to check for error condition err = MVar("error_command").get_val().split(",")    # error command to send to query error event debug = MVar("debug").get_val()                     # enable/disable debug prints             qdelay = 1.0                                        # delay for visa query``

Here we are just reading variables from the IC-CAP model variable table and storing them to local Python variables.

### Step 7. Use the high-level API function visaOpenSession to access the VISA Resource Manager and open a link to the E5270B resource

``# open session and return visa resource link vl = visaOpenSession(instr) if debug: print vl # close visa session bOk = visaCloseSession(vl) if debug: print "visaCloseSession: ",bOk``

You should always close the VISA session when you are done with a resource. Not properly closing the session can cause errors if you attempt to re-open the same resource later.

Execute your macro and you should see something like the following displayed in the IC-CAP Output window:

``default source manager: Resource Manager of Visa Library at C:\Windows\system32\visa32.dll resources list: (u'ASRL10::INSTR', u'GPIB0::17::INSTR') `pyvisawrapper::visaOpen: True inst: GPIBInstrument at GPIB0::17::INSTR `GPIBInstrument at GPIB0::17::INSTR `pyvisawrapper::closeSession: True `visaCloseSession:  True``

You did not need to import visa.py to access the VISA resources since pyvisawrapper.py handles that for you. It also handles calling the visa.ResourceManager() and visa.open() using your GPIB0:17::INSTR resource string specified in the Model Variable table.

If this step has errors, then you probably did not install and configure the GPIB VISA driver software application or the instrument is not on the bus. Otherwise the list will not return the GPIB resource with the address of your instrument. The communication drivers installed as part of the prerequisites allow you to 'scan for instruments.' National Instruments provides NI MAX (Measurement & Automation Explorer). Keysight interfaces provide Keysight Connection Expert. These software applications should be installed with your 488.2 and VISA drivers for your communications interface.

### Step 8. Send your first command to the instrument to return its identifier string

``cmd = "*IDN?" if bOk: bOk = visaQuery(vl, cmd, rsp, 10000, stat, err) print "visaQuery: {} cmd returned {}".format(cmd, rsp.get_val().split(","))``

Execute your macro and you should see something like the following displayed in the IC-CAP Output window:

``intr: GPIBInstrument at GPIB0::17::INSTR cmd: *IDN? rsp: <iccap.MVar instance at 0x000000000E11B608> timo: 10000 stat: 48 err: <iccap.MVar instance at 0x000000000E11B488> `pyvisawrapper::visaQuery() True read: Agilent Technologies,E5270B,0,B.01.10 `bytes: 39  `pyvisawrapper::visaQuery: True *IDN? returned ['Agilent Technologies', 'E5270B', '0', 'B.01.10\r\n']`

NOTE: The instrument was found on interface GPIB0 at address 17 and returned its identifier string in the visaQuery response.

### Step 9. Do some clean up and leave the system in an initialized state

It is good practice to send a GPIB device clear before closing the interface.

``bOk = visaClear(vl) `if debug: print "visaClear: ",bOK `

### Step 10. Test the completed _pyisa_run macro

Execute your macro and you should see something like the following displayed in the IC-CAP Output window:

``default source manager: Resource Manager of Visa Library at C:\Windows\system32\visa32.dll resources list: (u'ASRL10::INSTR', u'GPIB0::17::INSTR') `pyvisawrapper::visaOpen: True inst: GPIBInstrument at GPIB0::17::INSTR `GPIBInstrument at GPIB0::17::INSTR intr: GPIBInstrument at GPIB0::17::INSTR cmd: *IDN? rsp: <iccap.MVar instance at 0x000000000C39DF08> timo: 10000 stat: 48 err: ERR? `pyvisawrapper::visaQuery() True read: Agilent Technologies,E5270B,0,B.01.10 `bytes: 39  `pyvisawrapper::visaQuery: cmd *IDN? returned ['Agilent Technologies', 'E5270B', '0', 'B.01.10\r\n'] pyvisawrapper::clear: True inst: GPIBInstrument at GPIB0::17::INSTR  `visaClear: Truepyvisawrapper::closeSession: TruevisaCloseSession: True``

## Example of pyvisawrapper's Error Processing

Now, let's look at an example of the pyvisawrapper.py error processing, which will help you find errors occurring in your code. Again, this is one of the real advantages of using the pyvisawrapper API. Without it, you would have to write instrument specific code in your Python script to detect, query and display any errors.

Let's purposely create an example of a common type of error such as sending an illegal argument or command to the instrument, and see how it is handled by the pyvisawrapper.  Let's change our macro slightly to send the command "ID?" instead of "*IDN?"

Change the following lines to your macro:

``cmd = "ID?" if bOk: bOk = visaQuery(vl, cmd, rsp, 10000, stat, err) print "visaQuery: {} cmd returned {}".format(cmd, rsp.get_val().split(","))``

See the error on line 20?

Execute your macro and you should see something like the following displayed in the IC-CAP Output window:

``intr: GPIBInstrument at GPIB0::17::INSTR cmd: ID? rsp: <iccap.MVar instance at 0x000000000BFF0348> timo: 10000 stat: 48 err: ERR? Query error VI_ERROR_TMO (-1073807339): Timeout expired before operation completed. - last command: ID? visaQuery: cmd ID? returned ['visaError : 100 : Undefined GPIB command.\r\n']``

With no error checking, we would only get the timeout error "`VI_ERROR_TMO (-1073807339)`".  That doesn't say much, does it?

## What's Next?

If you've successfully completed all of these steps, then you should now be able to access the most common functions of PyVISA, the pyvisawrapper API from IC-CAP. And that means you can now very simply create new transforms for instrument control and data acquisition over any supported interface.

In a future article, I’ll outline the steps for using the ideas presented here to create a series of transforms to perform an Id versus Vg quasi-static measurement on a discrete NMOS device. In other forthcoming articles, I'll show you how to use more of the powerful features included in IC-CAP and exposed through the iccap.py. I'll also use the pyvisawrapper.py again to write Python code for accessing an instrument and returning measured results. I'll even show you how to access more of the internal IC-CAP functions and data structures, which are provided to enable additional analysis and plotting functions in IC-CAP.

In the meantime, I hope you find this tutorial and the pyvisawrapper utility useful in exploring the various possible ways you can extend IC-CAPs powerful framework to characterize and model your most challenging devices. For more information on IC-CAP or Keysight IO Libraries, go to www.keysight.com/find/eesof-iccap and www.keysight.com/find/IOlibraries.

## References

Extending the Power of IC-CAP with Python - PyVISA Instrument Control

Keysight IC-CAP Device Modeling Software

Keysight IO Libraries Suite

The Python Tutorial — Python 2.7.13 documentation

User Guide — virtualenv 15.1.0 documentation

PyVISA: Control your instruments with Python — PyVISA 1.8 documentation

By date: By tag: