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

EEsof EDA

129 posts

Abstract

The IC-CAP native data format, MDM is very comprehensive. All supported Inputs and Outputs data and related information are included in the saved file so that when importing the file back into IC-CAP, the Setup Inputs / Outputs page is completely defined, including all the necessary sweep data that are necessary for simulation and measurements. Over the years, more features have been added to MDM so that it is also possible to import and export variables and transform’s data results. However, many IC-CAP users have asked us to provide the ability to import data from a more standard table-like format, like CSV. While a CSV file only includes the data portion, it is useful to be able to import table-like data generated by other measurement software or export data in CSV format to be used by other tools. In this article, we will introduce a simple generic import/export CSV tool developed in Python. We will see that while exporting Inputs/Outputs information to a CSV file is relatively straight forward and can easily be generalized, the importing portion needs to be specialized. The CSV IO Python library and MDL example are attached and can work with the current version of IC-CAP 2020 Update 2.0. In the future, we are planning to integrate this in the next release so it can work out-of-the-box.


Introduction

Let’s consider a simple .csv file which stores data for a classic Ids vs. Vgs, Vds graphic of a MOS device. Fig. 1 shows the first portion of the file, note the first row representing the column headers. All numbers are separated by commas; however, other separators may be used.

 

Figure 1: Header portion of a typical CSV file.

 

First, observe that it is not possible to distinguish Inputs (or stimulus) from Outputs just by looking at the file, of course in this case the ids column represents the only Output, but a typical CSV file may have several output columns. In our module, the user will need to define all possible inputs and outputs columns in dictionaries. This is part of the initial setup.

Second, each row of data in the table represents a completely independent bias point. In this case, by inspecting the data, we observe that VDS is the second-order sweep, VGS is the first-order sweep and VBS is constant and set to 0. In other cases, VBS could be a third order sweep. Each sweep could be linear, log, or an arbitrary list of values. Furthermore, there is no guarantee that each second-order curve has the same number of points, i.e. data could be non-rectangular.

Based on the above observations, the safest and easiest options is to define table-like Inputs/Outputs by using the LIST/LSYNC Inputs.

Starting with IC-CAP 2020 Update 2.0, LSYNC is now enabled for Inputs of mode Voltage and Current so the importing script can directly define LIST/LSYNC Inputs to mimic the structure in the CSV file.

Note that if the file includes columns representing instance parameters such as L, W, NF, or Temperature, these need to be translated into Parameter sweeps so the importer will need to be further enhanced to be able to handle these. In the same way, if the CSV file includes frequency and S-parameters, the complexity further increases. In this example, we want to keep things simple by design so that we can learn how things work and provide some foundations for further development. The importer can then be further enhanced to support a custom CSV file or a more complex s2p file.

 

Installing the CSV IO Module In IC-CAP 2020 Update 2.0 and prior versions

The attached package provides the following files

 

  • csvio.py – This is the main python module to be imported. It includes the API functions that can be called within an IC-CAP Python transform to Import or Export from/to a CSV file.
  • ic_ioutils.py – A utility module used by csvio.py. Includes utility functions that can be used by other importers, such as a function to read a CSV file into a NumPy ndarray or functions to delete Inputs and Outputs in a setup.
  • csv_io_example.mdl – A simple example file that shows how to import or export from/to .csv files.
  • idvg.csv, idvd.csv – data files used by the above example.

 

Exit IC-CAP before installing the modules.

 

In the following, we will assume that all the py files are saved in the installation directory. $ICCAP_ROOT/iccap/lib/python. 

 

Alternatively, if you can’t modify the IC-CAP installation, you can define the environment variable ICCAP_USER_PYTHON_PATH to point to a local directory where you have edit permission and save csvio.py and ic_iutils.py in that directory.

 

Importing CSV files

Next, save the example data files in a local directory. Once the files are installed, start IC-CAP and load the .mdl file.

 

The model file named nmos_bsim includes a simple BSIM3 model card for demonstration purposes. Select the transform DC_dut/idvg/import_csv. Before you execute it, make sure to modify the file path to reflect the actual location of the idvg.csv file to be loaded. You may also delete all the Inputs and Outputs in the Measure /Simulate page to create a clean initial state (the script will delete existing Inputs/Outputs anyway before creating new ones).

 

Figure 2: The import_csv function

 

Note that all the program complexity is in the python module (we’ll review the Python script later) so it is straightforward for the user to import the data. Go ahead and execute the function. Switch to the Measure / Simulate Tab to see the newly created Inputs and Output. Display the plot to see the data. In the Plot definition, it is important to set Curve Data to the second-order sweep, Vds so that the curves are correctly displayed.

 

 

Figure 3: Inputs / Outputs page and Plot after importing idvg.csv

 

Data are imported in the current Setup by using a combination of LIST and LSYNC Inputs.  IC-CAP does not directly support an Input sweep of type ‘TABLE’ however, table-like simulations can be achieved by using LIST and LSYNC. Since Vbs is constant throughout the table, instead of using an additional LSYNC Input, the algorithm converted Vbs to an Input of Sweep Type CONST.

In the same setup, find the transform import_csv_1. This function will also import the idvg.csv file, however, it uses a different module function called csv_file_import_create_noprompt() which receives the name of the destination setup as its first argument in addition to the name of the input file. You may call this function from other transforms or macros to automate data import across several setups.

Finally, note that since the Input nodes are correctly defined during the import, and are consistent with the current model card, the setup can be directly simulated. We’ll see how this is achieved by correctly defining the Inputs and Output dictionaries in the csvio.py module.

Now switch to the idvd Setup and use the transforms import_csv and import_csv_1 to import the sample data. The plot should look like Fig. 4. Note that this time we used the plot_vs() function in the Plot definition. The plot_vs() is very important with table-like data since it clearly defines how to draw the data by specifying the first, second, and higher-order sweeps.

 

 

Figure 4: Ids vs. Vds, Vgs plot after importing measured data.

 

One final note on the Import is that the imported data are always imported in the Measured portion of the Output arrays.

 

Before providing more details on the csvio.py module, let’s discuss how to export to CSV files.

 

Exporting to CSV files

 

Exporting to CSV is executed by the export_csv transform located in both idvd and idvg setups.

Figure 5: export_csv transform in the idg setup

 

The function has two arguments, the name of the file and an optional list of arguments. In this release of the library, only the supported options to select the type of data to be exported to the CSV file are ‘M’ and ‘S’.

 

Edit the path and the name of the file and run the transform to save the current setup data.

 

Like the import case, the transform export_csv_1 uses the csvio.py module function csv_file_export_noprompt(), which has an additional argument to enable saving to a different setup.

 

Data to be exported to a CSV file do not need to be organized using LIST and LSYNC. Data related to different types of Inputs or Outputs are still organized in arrays (or columns) in the underlying IC-CAP data structure. For example, when viewing data of a linear sweep, the data are stored as an array. LINEAR, CONST, LIST sweeps can easily be exported to CSV by using this Python module. However, Outputs of type S-parameters or in general Outputs storing complex data are not supported since the current csvio.py module does not support splitting complex data into two or more columns such as the case of 2-port S-parameters, where each frequency point would need to be split into 8 columns.

 

The CSV IO Python module

 

Let’s now take a closer look at the Python module. Before you can use the module to import your CSV files, you will need to add your input and output definitions. This is done in the first lines of the file, in the “Constant and Settings” section. Find the INPUTS_DEF and OUTPUT_DEF dictionaries. You will need to add your columns headers to these. Restart IC-CAP after you have edited and saved the file to allow the IC-CAP Python interpreter to compile the modified file py file.

 

Figure 6: INPUTS_DEFS dictionary

 

Add a new entry in the dictionary for each of your column headers. The key is the column name found in the CSV file (e.g. 'VDS') the value is a tuple that has three elements: the desired name of the Input in IC-CAP (e.g. 'Vds') the Input Mode (‘V’ or ‘I’) and the Input Node. The Input Name, Mode, and the Node can be changed later once the data are imported but it may cause loss of data if the renaming is not done in the correct sequence since LSYNC Inputs are linked to the master LIST sweep by name. I recommend defining these entries here so that Inputs do not need to be edited later in IC-CAP.

 

Figure 7: OUTPUTS_DEFS dictionary

 

Similarly, add Output entries to the OUTPUTS_DEF dictionary. Modes can be current (I), voltage (V), capacitance (C), etc. Keep in mind that only real Outputs are supported (e.g. S-parameters are not supported)

 

The USE_CONSTANT_INPUTS flag is set to True by default, change it to False if you prefer all the Inputs in LIST/LSYNC format, even if their values are the same at each row.

 

The function load_cvs_file_internal() is the main function executing the importing. First, it calls the function read_data() to read the file. This function is defined in ic_utils and can read table-like data with different delimiters (comma, white spaces, tab, etc.) so this importer will work even if the comma is not used as a delimiter.  Read_data returns a 2D array of data and the column header.

 

Next, the 2D data array is transposed so that each array is a column. This way it is easier to map the data to each Input or Output.

 

After deleting the existing Inputs, the function create_input_csv() creates all the new Inputs and fills them with data. The first column in the file will become the master sweep of type LIST, the other columns will be LSYNC. Note that the LIST Input will be assigned a sweep order equals to 1, however, LIST/LSYNC Inputs are tied together in a table-like format so for simulation purposes the order is not used. For plotting, any column can be used as the first-order sweep as long as the desired order is specified in the plot_vs() function.

 

Finally, Output are created by the create_output_csv() function. The create_output() function in the ic_ioutils module will do the heavy-lifting by creating the various Outputs and filling them with data.

 

The export is executed by the export_csv_file_internal() function and it is fairly straight forward. After collecting the Input and Output names, these are passed to the get_data_array() function which collects the data from the Inputs and Outputs objects and creates the 2D arrays to be passed to the save_to_file() function.

 

Note that by using NumPy array, we can conveniently use the np.savetxt() function to directly write the file.

 

Coding becomes more complicated if you need to support frequency and complex outputs such as S-parameters since frequency needs to be defined as a LIST of sweep order 1 to be able to simulate correctly with LSYNC Inputs. S-parameters are likely saved using 8 columns and need to be compacted into a single 2x2 data array point in IC-CAP. In the same way, export to CSV becomes more complex as each S-parameter data point needs to be split into several columns.

 

Summary

I have introduced a simple Python module to enable importing and exporting CSV files within IC-CAP. With a simple initial setting, it can be used to import basic DC and CV files. The module and the examples are attached below. The module could be improved to include import for columns representing parameters (e.g. L, W, NF) and temperature. Also, to improve the simulation speed, the module could analyze and recognize sweeps to be implemented using LINEAR or LOG Sweep Types. Finally, as a further improvement, the module could be enhanced to support complex data such as S-parameters vs. frequency, although a dedicated importer for s2p or sNp files would be preferred. 

 

Note: the installation instructions and the downloads have been updated after initial publication to work with IC-CAP 2020 Update 2.0 (current release at the time of writing this blog). New installation instructions will be provided for the upcoming IC-CAP 2010 Update 2.1 since the structure of the $ICCAP_ROOT/iccap/lib/python directory will be modified. 

Pulsed measured data are key to investigate and model important effects such as thermal, trapping/memory. Due to self-heating, using a Pulsed System is sometimes the only way to measure large RF power devices. Pulsed IV data are often represented by multi-curve data in which each curve has a different number of points and the values of the independent variable (i.e the first-order sweep, typically Vd) may also vary at each curve. This data is often called non-rectangular and stored in a table-like format. Until now, it has been challenging to manage non-rectangular data within IC-CAP and even more so, to simulate it. In this article, I’ll discuss new features released in IC-CAP 2020 Update 2.0 that greatly facilitate importing and simulating table-like data.

A PDF version of this article is attached along with an IC-CAP example project.


Introduction

A classic example of a non-rectangular data structure is obtained when measuring the steady-state (i.e. non-pulsed) Id vs. Vd, Vg characteristics of a FET device using power compliance to avoid damaging the device. In this case, each curve will typically have a different number of points with curves in the high-voltage / low-current regions extending to higher drain voltages than those that intersect the high-power regions. To add complexity, sometimes the Vd points may not be equally spaced to provide more resolution in the low-voltage region.

 

Figure 1: Example of non-rectangular data (DC IV Non-pulsed with power compliance)

 

Figure 2: IC-CAP classic IV Setup storing rectangular data


DC IV data measured with a Pulsed System are similar to the characteristics measured with power compliance.  Each curve could have a different number of points and because of the system losses, the Vd points may not be in a regular grid and their values may change in each curve. Some systems can adjust or tune the bias at each point so that the actual Vd at the device port matches the desired Vd by using a software algorithm. This slows down the measurement, but it results in a more regular plot where all curves share the same Vd values.

 

The traditional IC-CAP Setup for Id vs. Vd, Vg is shown in Fig. 2 and uses Inputs of Sweep Type: “Linear” where Vd and Vg are the first and second-order sweeps, respectively.  This setup creates a regular rectangular grid and does not align with Pulsed IV measured data.  Alternatively, one could use a ‘LIST’ sweep to represent all the Vd values and while this list would allow one to define arbitrary values for each curve since the entire Vd would be a 1st order sweep, all the values in the list would be simulated for each Vg value. This would result in a redundant number of bias points simulated and would require data to be manipulated before they can be displayed.

 

A solution to our non-rectangular data problem is to use a combination of LIST and LSYNC Inputs to represent table-like data to be simulated on a row basis.  In tabular data, each column represents an Input or an Output and each row represents an independent simulated bias point. IC-CAP does not directly support an Input sweep of type ‘TABLE’ however, table-like simulations can be achieved by using LIST and LSYNC.

 

In the latest release, IC-CAP 2020 Update 2.0, LIST/LSYNC Inputs have been enhanced to support direct simulation of I/V table-like data, including temperature and instance parameter information. In this paper, I will first discuss how to import pulsed data into IC-CAP, then I will discuss how to simulate it.

 

Importing and displaying Pulsed Data into IC-CAP

 

IC-CAP 2020 update 2.0 includes a new Python module called ‘pivimport’. This module allows the import of two types of data files:

  • Maury/AMCAD DC/Pulsed measurement data file. (.mes)
  • Maury/AMCAD S-parameter measurement data file (.mps)

I will not discuss the details of the .mes and .mps formats here. Both files include blocks of data, each representing a DC or an S-parameter curve at different Vg (.mes) or different Vg and Vd (.mps) values so the file reader needs to be able to parse the bias information, create LIST and LSYNC Inputs accordingly and finally read the data into the Outputs. If you are curious about the implementation, please see the open-source pivimport.py file.

An example MDL file, PIV_IMPORT_Example_FET.mdl is provided under the directory:

$ICCAP_ROOT\examples\new_features\ICCAP_2020_Update2

 

Figure 3: Example of Python transform importing (.mes) data


In the example project, there are two DUT’s: dev_p and dev_iv. Here, we will consider the dev_iv, which uses the latest improved implementation of LSYNC shipped with IC-CAP 2020 Update 2.0.

To load .mes file, run the transform test_import_mes. 

At the prompt, select the .mes file. Data will be loaded in the current setup. Copy and paste the transform into your project to be able to load data into another project Setup or simply provide a different destination Setup (dest_setup in line 7 above) as first argument.

 

Figure 4: Input / Output page after importing a .mes file (DC IV pulsed data)

 

In the transform, note that the new Python module pivimport is imported on line 1. After loading, the Inputs / Outputs page should look like Fig. 4.

Note how the combined use of LIST and LSYNC Inputs creates a table-like structure. The vd_pulse Input is of type LIST. Think of it as the master column or the first column of the table. Inputs of type LSYNC are ‘linked’ to the master vd_pulse and represent the other columns in the table. You can have many LSYNC (additional columns in the table) but there is only one master LIST. LSYNC may be of type V/I or P (Parameter). Type P may represent entries like temperature, width, length, etc. If necessary, Inputs of type ‘CONST’ are also allowed and they represent columns in the table in which each cell has a constant value.

 

 

Figure 5: Using Curve Data and switching the legend on and showing the marker information

 

Each curve populates a section of the table. In this case, rows representing a single curve will have the same values for vg_pulse (this is how IC-CAP knows how to correctly display the curves). In fact, there are a couple of ways to display the plot.  In the first method shown on the left in figure 5, we use “Curve Data”. By setting Curve Data to vg_pulse, we tell IC-CAP that vg_pulse is to be used as a 2nd order sweep.  When its value changes, the IC-CAP graphic engine knows that it needs to start a new curve. Note also how both legend and the marker info display work well.

The second method is somewhat more powerful, especially if you have more than two LSYNC and a third-order sweep (i.e. several columns in the table).  This method uses the new plot_vs() function. See Figure 6 and note the definition of ‘Y data 0’.

 

Figure 6: Using plot_vs() to define the trace

 

Importing S-parameter data follows a similar procedure. Maury’s .mps files are essentially touchstone files that include bias information in the header of each frequency block. Since the tabular format only refers to the bias points, the IC-CAP importer requires each frequency block to have the same number and values of frequency points.

 

Figure 7: Typical S-parameter Setup imported from .mps files

 

In the example file, use the transform in the setup dev_iv/piv_iv_spar_mps to import .mps file. After importing the setup should look like Fig. 7.

The frequency is the first order sweep and is a LIST Input since frequency may not be a linear sweep in the file. The bias is implemented as tabular data using the LIST/LSYNC. At simulation time, the frequency sweep is executed for each row of the tabular data, representing a single bias point.

 

As mentioned earlier, the new pivimport.py module is installed into the $ICCAP_ROOT/lib/iccap/python/icutils directory. It is open-source and can be easily viewed to see how the import algorithm is implemented and to use as a starting example to import other types of table-like file formats. To support the tabular nature of the data, simulations are run row-by-row.

 

Simulating Tabular and Pulsed Data in IC-CAP

 

Once LIST/LSYNC Inputs have been created as explained in the previous section, IC-CAP 2020 Update 2.0 can correctly simulate the data. At this time, the new LSYNC I/V/S feature is supported with ADS (including spmodeads and hspicemodeads netlist syntax modes) and ELDO.

If the tabular data were measured under steady-state conditions (e.g. power compliance measurements, Maury/AMCAD DC IV characteristics, etc.) then no further considerations are necessary when the model self-heating model is turned on. Providing that RTH has been extracted, the simulation will automatically consider the effect of self-heating.

On the other hand, if the measured data are from Pulsed IV or Pulsed S-parameters measurements, then further considerations are necessary. Let’s discuss the Pulsed IV case first. We will assume that data are quasi-isothermal, i.e. were measured using narrow pulses (e.g. 500ns or less) and that the temperature increase is negligible during the pulse. Also, the pulse period is long enough so that there is no significant temperature change over time. Typically, this condition is satisfied if the duty cycle is 1/1000.

If the device does not show any trapping/memory effects or the quiescent bias point is Vg=Vd=0 (no trapping effects are excited at quiescent) then a simple way to simulate is to turn off self-heating. This can easily be achieved by setting RTH=0 in the Model Parameter Table and executing a classic DC simulation.

If the device has memory effects, like modern GaN devices, modeling engineers will typically perform Pulsed IV measurements at different quiescent points to study the effect of traps and extract the related model parameters. In this case, we cannot use standard DC simulations, but rather we need to extract the Pulsed IV characteristics from transient simulations, just like in a real Pulsed System. For each bias point, a pulsed transient simulation is run, and one value is sampled within the id pulse. The sampled current values are then combined to re-construct the IV characteristics. This type of simulation is not natively supported by ADS and since it comprises of many transient simulations, it is more time-consuming than a regular DC simulation.

This is where the power and flexibility of IC-CAP can greatly help.

The DUT Circuit is shown in Fig. 8. A 4-terminal GaN device is defined at the Model circuit level using an ASM-HEMT model card (not shown here). At the DUT level, we define a sub-circuit that includes voltage sources at the drain and gate terminals. These sources use the erf_pulse() function in ADS which defines a pulse waveform in the transient simulation. The parameters governing the pulse (e.g. quiescent and pulse levels, pulse delay and width) are defined as parameters of the sub-circuit

 

 Figure 8: DUT Circuit for Pulsed IV simulation in the time domain

 

The Measure / Simulate page tab is shown in Figure 9. The Input time which defines the transient simulation and the time interval. The constant Inputs vg_qu and vd_qu define the Vg and Vd quiescent point (-3.3, 28) and the LIST/LSYNC Inputs define the Vg and Vd pulse levels. These are typically imported from measured data.

Running this simulation is only the first step required to obtain the Pulsed IV characteristics. In fact, this simulation Setup will output as many ig and id waveforms as the number of bias points (243 in the example). Fig. 10 shows the id.s waveform as a function of time. Just as in a real Pulse Measurement System, to create the IV characteristics, we need to sample each waveform at a specific time delay; not too early so that the current level is not affected by the turn-on transient, but also not too late otherwise, self-heating may start to affect the current level. It is important to observe that the waveform measured by the system may look different due to the presence of additional parasitics (e.g. cables, connectors, etc.)Note the two auxiliary Inputs vg_aux and vd_aux. They define two voltage sources and have no effect; however, they are necessary so that IC-CAP can read the current flowing through the G and D nodes. The Outputs id and ig monitor the currents and finally, if the model supports self-heating, one can read the temperature node. If you turn on self-heating, make sure that you have a reasonable value for CTH, otherwise, the transistor will heat-up instantaneously during the short pulse and follow the instantaneous dissipated power.

 

Figure 9: Inputs / Outputs configuration to simulate Pulsed IV characteristics


If no trapping is present, i.e. the quiescent bias point is (0,0) one can use pulsed data to extract CTH by comparing measured and simulated data of different pulse widths and sampling points.  In a typical III-V device, the slope of the current along the pulse can be used to determine the thermal capacitance and resistance. In Figure 10, note the slight negative slope during the pulse at high currents due to self-heating.

 

Figure 10: Simulation of Pulsed IV Characteristics

 

If the RTH and CTH have been previously extracted, and you are investigating trapping, then use the same simulation time delay as used in the measurement.

In the Extract / Optimize page, the transforms id_vs_vd and ig_vs_vd sample the waveforms and return the current dataset to be used for display purposes as shown in Figure 11. These are relatively simple to implement and are provided in the attached example project.

 

Figure 11: Simulation of Pulsed IV Characteristics

 

If the model includes trapping, you will obtain different Pulsed IV characteristics depending on the quiescent point and can use these curves to extract the related model parameters. At the time of this writing, investigation on the trapping model used in this example, the CMC standard ASM-HEMT model for GaN devices, is still under undergoing.  Therefore, we are currently not able to display pulsed IV curves showing different trapping states.

 

Finally, a word on simulating Pulsed S-parameters. Unfortunately, a full simulation of Pulsed S-parameters using a combination of time-domain and S-parameters is not possible since no commercial simulator supports this analysis combination. Typically, Pulsed S-parameter measured data are compared to S-parameter simulation with no self-heating. If the device has memory effects, use only data measured at (0,0) quiescent point to minimize trapping effects.

 

To study the effect of trapping on RF behavior and validate the model, one should compare results from large-signal Harmonic Balance (HB) simulations to waveforms from active load-pull measurements such as those measured by the Keysight Nonlinear Vector Analyzer.

 

Note: The attached example requires IC-CAP 2020 Update 2.1 scheduled to be released in May 2020 due to a change in the python library directory structure.

If you are running IC-CAP 2020 Update 2, simply replace the line:

from icutil import pivimport

with:

import pivimport

in the Python transform used to import .mes and .mps file.

The Tower of Babel (Genesis 11:1-9) is a story to explain why the world's peoples speak different languages:

 

 

https://en.wikipedia.org/wiki/Tower_of_Babel

 

The explanation of why the world's circuit simulators speak different languages is more mundane...

 

There were many attempts at circuit simulation programs before, but one called Simulation Program with Integrated Circuit Emphasis (SPICE) really pushed all the others out soon after it "... was announced to the world … in Waterloo, Canada at the Sixteenth Midwest Symposium on Circuit Theory on April 12, 1973. The paper was presented by none other than Professor Donald O. Pederson of the University of California, Berkeley." (As one of the co-authors Larry Nagel recalls in his retrospective, "Life of SPICE.")

 

Happy Birthday, SPICE, 46 years old today!

 

The original SPICE, now called Berkeley SPICE, went through many versions but in all of them the input "deck" (yes, early computer programs were on a deck of punched cards) remained fairly consistent. The circuit you wanted to solve was in the form of a netlist:

*** NETLIST Description ***
M1 vdd ng 0 0 nm W=3u L=3u
R1 in ng 50
Vdd vdd 0 5
Vin in 0 2.5

Some simple models were "atomic" (R for resistor, V for voltage source), other more complex ones (M) refered to a separate .model line:

*** MODEL Descriptions ***
.model nm NMOS level=2 VT0=0.7 KP=80e-6 LAMBDA=0.01

Then you just had to specify what analysis you wanted and where the last card was:

*** SIMULATION Commands ***

.op
.end

 

But then the Tower of SPICE got too close to the heavens and suffered a set back as punishment for its hubris! Here is what happened: UCB had an open source policy that enabled anyone to freely modify, compile, and sell the resulting binary. Twins siblings Ashawna and Kim Hailey founded Meta Software to sell HSPICE. (It's now owned by Synopsys). A version by Microsim for PC was called PSPICE. (It's now owned by Cadence). Linear Tech (now part of ADI) had LTspice. There was ISPICE, ISSPICE, XSPICE and of course Keysight's (then HP's) very own high frequency HFSPICE (since renamed ADS Transient Convolution).

 

The benefit was free market competition to improve it, but a frustrating side effect was the input dialect diverged to the point that a netlist written for one version would no longer run on any other.

 

So, just like human languages post-Babel, translators came in. ADS has a part called (you guessed it) "netlist translator" or nettrans. If you have a PSPICE/LTspice netlist model that consists of open, basic components such as RLC, VCCS, industry-standard models and so on, then we can import it. If it contains encrypted parts or proprietary compiled C-code models we cannot. Sometimes there is a workaround: we can find a similar model, or tweak the syntax to make it work. Ask us!

 

What about HSPICE and Spectre? For those, translation was found to be inadequate so we added "compatibility modes" to our simulator that actually change its internal behavior so it can consume the foreign netlist directly and without translation. Again, there are a few proprietary features that we can't support, but we'll do our best to help you. Some things really are Lost in Translation!

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

 

Bring your questions to our live Q&A session!

 

Registration link:

https://event.on24.com/wcc/r/1858242/33F2A49D563423ACD54D859E4806BAF1

Abstract:

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

Three Key Learnings: 

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

Hope to see you there!

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

 

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

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

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

 

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

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

 

 

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

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

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

      i0: REAL := 0.0; -- amps

      tau: REAL := 0.0; -- seconds

      c0: REAL := 0.0; -- farads

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

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

 

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

 

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

   quantity vdiode across idiode, icap through a to k;

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

   icap == qcap’dot;

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

 

My translation to Verilog-A is:

`include "disciplines.vams"

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

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

 

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

 

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

 

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

 

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

 

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

 

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

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

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

What is a Derivative?


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

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

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

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

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

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

 

 

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

 

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

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

 

 

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


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

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

print der

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

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.

 

iccap_derivative_comparison_plot

 

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

 

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

 

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

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

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

   # compute the derivative
   dydx = ddy/ddx
   return dydx

 

 

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

 

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

 

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

 

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

 

# forward difference approximation

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

if debug: print dydx

 

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

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

 

set_return_array("M", gm)

 

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

 

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

 

 

 

iccap derivative comparison table

 

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.  

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

Shared Library

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

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

 

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

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

 

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

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

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

All-in-one method

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

Success!

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

 

Note 1: In Step 10 above, if you want to use the Auto-Generate tab instead of the Copy/Modify tab, you'll have to add an extra step to specify the number of pins. Create a schematic view in the cell you are working on, then place one pin for every port in the Verilog-A function argument list e.g. 2 pins for module resistor(p, n); . It looks odd to have a schematic with nothin but unconnected pins, but trust me it works! Then save the schematic and go back the symbol view Auto-Generate tab.

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

 

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

 

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

 

Code fragment 1a: Resistor in VHDL-A

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

 

Code fragment 1b: Resistor in Verilog-A

`include "disciplines.vams"

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

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

use electrical_system.all;

…and Verilog-A has:

`include "disciplines.vams"

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

 

The two languages diverge in the next lines.

 

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

quantity vr across ir through p to n

 

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

 

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

 

 vr == ir * r

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

 

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

The line:

V(p,n) <+ I(p,n) * r;

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

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

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

…is equivalent to:

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

 

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

 

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

 

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

 

 

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

 

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

 

Setup Variables in IC-CAP

 

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

 

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

 

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

 

Measure/Simulate Tab


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

 

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.  

 

Load the IC-CAP Model File

 

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

 

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

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

# get setup variables
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. 

 

idvg_quasi Init transform

 

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

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

 

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

 

Setup Object and Methods

 

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

 

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

 

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

 

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

 

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

 

Setup query response:

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

name: idvg_quasi

mytype: Setup

 

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

 

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

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

Sweep query response:

Sweep 1 /py_api_demo_5270/dc/idvg_quasi

 

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

 

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

 

# query child objects and type
if debug:
   print "child objects of Setup:"
   for objchildtype in s.childtypetuple:
      print objchildtype
      for objchild in s.get_child_objects(objchildtype):
         print " ", 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

      README

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 input
sweepName = vg.get_fullname()

 

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

 

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

 

We now have pointers to the following:

 

iccap_idvg_quasi_input_vg

 

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

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

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

 

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

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

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

name : Start

mytype : TableVar

value : 0.0

 

...

 

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

 

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

 

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

 

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

 

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

 

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

 

Querying Constant Input Variable Properties

 

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

 

vd = Input(setupName + "/vd")

 

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

 

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

 

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

 

 

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

 

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

 

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

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

 

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

 

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

name : Compliance

type : TableVar

value : 0.1

 

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

name : Value

mytype : TableVar

value : 3.0

 

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

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

 

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

 

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

 

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

 

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.

 

iccap_idvg_quasi_run

 

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


Related Links

 

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

 

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

 

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

 

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

 

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

 

For Power Devices:

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

 

For RF Devices:

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

 

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

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

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

 

In my previous article, I showed how to implement a quasi-static measurement in a series of IC-CAP transforms, demonstrating the flexibility of the data handling capabilities in IC-CAP.  Python modules like 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.

E5270BAgilent 4156C

 

Each model file contains the complete Python source code used to implement the quasi-static measurement. I recommend saving these *.mdl files to C:\Keysight\ICCAP_2018\examples\python, as I described in my previous article Measuring Sub-Threshold MOSFET Characteristics Using a Quasi-Static I-V Method. In these .mdl files we previously created the global arrays vvalue[ ] and ivalue[ ] in the _run transform for storing our forced voltage and measured current data values. In the _calc_vth transform we will access these arrays to extract the Vth value by calling an external Python script 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 values
vvalue = []
ivalue = []

...

 

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

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

 

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

...

 

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

 

from extract_vth_cc import * 
#################################################################
# _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 np
from iccap import MVar
##############################################################################
#
# Global variables
#
##############################################################################
# enable/disable debug prints
debug = int(MVar("debug").get_val()) 

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

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

       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.

 

Related Links

 

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

How to Extract Threshold Voltage of MOSFETs

Python Programming Integration with IC-CAP

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

 

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

 

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

 

Quasi-static DC IV

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

 

fig.1. Quasi-static Measurement

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

 

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

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

 

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

 

Writing a Set of Custom Transforms in IC-CAP

 

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

 

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

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

 

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

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

>>>

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

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

You should see something like the following:

C:\Users\username\Envs\icenv2018

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()

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

Leave the virtual environment by typing:

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

C:\Users\username\Envs\icenv2018>

 

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

 

iccap_file_open_mdl_5270

 

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

 

Click on the 'Model Variables' Tab

 

 

 

 

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

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

This enables the reporting of useful diagnostic information.

 

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

 

 

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

 

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

 idvg_quasi setup variables table

 

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

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

 

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

 

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

 

The following transforms are defined:

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

 

Note: The _run Transform contains code calling the '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.

 

idvg_quasi setup _run transform

 

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

 

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

 

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

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

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

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

 

There are several necessary lines in the _run script that import the correct Python modules for use in the other transforms. The first two statements in the script below activate and execute the Python interpreter in the virtualenv named icenv2018 that was previously installed. (As mentioned earlier, the path to the '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, TableVar
from pyvisawrapper import *
from numpy import *
import itertools
import pathlib2 as pl
import csv
import math
import time
import imp

 

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

 

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

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

...
  

  

 

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

 

# defaults from the model variables table

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

...

 

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

 

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

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

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

 

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

 

 

Open _init transform

 

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

 

iccap_setup_idvg_quasi_init

 

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.

 

iccap_setup_idvg_quasi_setup_iv

 

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.

 

iccap_setup_idvg_quasi_meas_iv

 

# 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 values
vvalue = []
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.

 

create_idvg_plot

 

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.

 

Related Links

 

rajsodhi, co-author

ShuangCai