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


130 posts

In this video clip, you learn how to use a power device reference design to accelerate you next SMPS project.

After watching the clip, you can download the User Guide


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.


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


  • – 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.
  • – A utility module used by 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 and 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 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 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 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 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.



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.


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

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



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


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:


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 ***



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:


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

Three Key Learnings: 

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

Hope to see you there!

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


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

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

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


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

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



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

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

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

      i0: REAL := 0.0; -- amps

      tau: REAL := 0.0; -- seconds

      c0: REAL := 0.0; -- farads

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

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


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


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

   quantity vdiode across idiode, icap through a to k;

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

   icap == qcap’dot;

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


My translation to Verilog-A is:

`include "disciplines.vams"

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

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


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


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


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


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


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


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

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

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

What is a Derivative?

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

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

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

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

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

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



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


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

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



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

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

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

print der

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

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


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


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


import numpy as np

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

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

   return dydx


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




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

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


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


# forward difference approximation

dydx = m.icm_derivative_npdiff(x,y)

if debug: print dydx


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

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


# insert dydx starting element to gm at index = 0

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


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


set_return_array("M", gm)


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


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




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


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


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

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

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

   # compute the derivative
   dydx = ddy/ddx
   return dydx



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


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


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


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


# forward difference approximation

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

if debug: print dydx


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

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


set_return_array("M", gm)


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


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




iccap derivative comparison table



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

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

Shared Library

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

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


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

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


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

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

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

All-in-one method

  1. Launch ADS. From the ADS Main Window menu bar, select File-->New-->Workspace
  2. In the "New Workspace" dialog box, give your new workspace a name such as all_in1_wrk 
  3. Click on the "Create Workspace" button.
  4. In the Main window "Folder view" tab, you will see a workspace folder whose name ends in ...\all_in1_wrk. Right click on it and select the last option in the pop up context menu, namely "Explore In File System".
  5. Assuming Windows OS, File Explorer opens in the workspace's folder on your file system. Right click on a blank space in the right panel and select New-->Folder. It is best to name the folder veriloga because then it will be on the default search path that the Verilog-A compiler looks in.
  6. Open your new folder and, using copy-paste and your favorite plain text editor, create the file containing the Verilog-A source code from Part 1. Name the file (The first part can be anything convenient, but the file extension must be either *.va or *.vams for the compiler to recognize it.) You can close your text editor and Windows Explorer if you like.  
  7. Go back to the ADS Main window and from its menu bar, select File-->New-->Symbol view...
  8. In the "New Symbol" dialog box, overwrite the default cell name (cell_1) with  resistor. Note that you are creating not only a view but also a cell. A cell is a container that holds one or more views of a component or a circuit. You never view or edit the cell directly but instead you view and edit it via one of its views. It is important that the cell name, resistor, matches the module name, resistor, in the Verilog code.
  9. In this "New Symbol" dialog box, click the "Create Symbol" button. Two new windows open: The Symbol Generator dialog box and the Symbol canvas itself.
  10. In the "Symbol Generator" dialog box, make sure you are on the Copy/Modify tab, then select "Lumped-Components" from the "Symbol category" drop down list. (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:
  25. Click on the Simulate icon or select Simulate-->Simulate or just hit the F7 shortcut key. The first time you simulate, there is a short pause while ADS compiles the Verilog code. It saves the compiled model, so if you don't touch the code, there is no need to re-compile the next time you run it.
  26. From the Data Display window, insert a plot, and select the Vout and branch current (_ub_p_n_i) traces:
    voltage and current through 2 ohm resistor


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


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;
   vr == ir * r;
end architecture signal_flow;


Code fragment 1b: Resistor in Verilog-A

`include "disciplines.vams"

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

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

use electrical_system.all;

…and Verilog-A has:

`include "disciplines.vams"

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


The two languages diverge in the next lines.


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

quantity vr across ir through p to n


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


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


 vr == ir * r

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


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

The line:

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

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

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

…is equivalent to:

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


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


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


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



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


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


Setup Variables in IC-CAP


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


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


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


Measure/Simulate Tab

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




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


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

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

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


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


Load the IC-CAP Model File


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



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


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


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

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

# get setup variables



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


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


Exploring the _init transform


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


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


idvg_quasi Init transform


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

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


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


Setup Object and Methods


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


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


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


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


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


Setup query response:

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

name: idvg_quasi

mytype: Setup


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


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

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

Sweep query response:

Sweep 1 /py_api_demo_5270/dc/idvg_quasi


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


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


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


Child object query response:

child objects of Setup:




































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


Input/Output Objects


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


vg = Input(setupName + "/vg")


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


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


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


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


We now have pointers to the following:




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

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

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


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

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

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

name : Start

mytype : TableVar

value : 0.0




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


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


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


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


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


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


Querying Constant Input Variable Properties


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


vd = Input(setupName + "/vd")


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


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


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



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


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


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

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


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


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

name : Compliance

type : TableVar

value : 0.1


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

name : Value

mytype : TableVar

value : 3.0


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

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


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


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


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




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




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


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



IC-CAP 2018 user-manual and documentation

Example model file "py_api_demo.mdl" under Macros

Related Links


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


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


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


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


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


For Power Devices:

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


For RF Devices:

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


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

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

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


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


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




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

E5270BAgilent 4156C


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


Load the IC-CAP Model File


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


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

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


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



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

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


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



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


from extract_vth_cc import * 
# Calculate Threshold voltage for IdVg measurement

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

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

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

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

print "vth = ", vth


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


Open the Python source code in your favorite editor.


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

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

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

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

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

       Returns:       instance of class

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

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

             self:      This instance of the extract_vth_cc class.


             Vth:       Interpolated gate threshold voltage at icon.

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

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

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


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




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


Related Links


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

How to Extract Threshold Voltage of MOSFETs

Python Programming Integration with IC-CAP