Manuals >Reference >Drivers
Print version of this Book (PDF file)
prevnext

Adding Instrument Drivers to IC-CAP

Many instruments can measure a device or a circuit. While IC-CAP supports major HP/Agilent instruments, other instruments manufactured by HP/Agilent or other vendors could be used for characterization work within IC-CAP. The Open Measurement Interface (OMI) is part of IC-CAP's open system philosophy that allows the addition of new instrument drivers.


Note


Because creating new drivers requires using C++, you must obtain C++ software that will compile with both your operating system and with IC-CAP. To determine appropriate software media options and to obtain the most up-to-date part numbers, consult an appropriate pricing and configuration guide, or contact your sales representative.


This section provides information about OMI and the basic form of an OMI driver. Alternatives to creating a new driver are also addressed.

Using the Open Measurement Interface

The Open Measurement Interface enables you to add drivers for other instruments. User-added drivers can be full-featured, fully integrated, and indistinguishable from the Agilent-provided drivers. Like the Agilent-provided drivers, they are written using C++. OMI was designed to ensure that C Language programmers do not experience language barriers when creating new drivers.

Much of the work necessary to lay out the required code is performed by a tool kit comprised of Driver Generation Scripts described in Adding a Driver. These scripts also write all necessary code for the Instrument Options editors for a new driver, and all necessary code for the driver to be included in the Instruments Library shown in the Hardware Setup window.

The user is responsible for filling in the bodies of a set of functions that IC-CAP calls during measurements. A set of reusable software constructs is provided for accomplishing common programming tasks; refer to Programming with C++.

With the first version of the Open Measurement Interface (IC-CAP version 4.00), only GPIB based instrument I/O is formally supported.

OMI Guidelines

To use the Open Measurement Interface, the following qualifications are recommended.

    • One year of C programming experience or recent completion of a good course in C. Familiarity with the use of struct data types in C (or record data types in PASCAL) is essential, because C++ classes build upon the struct concept.
    • Experience writing code to control an instrument.
    • Familiarity with the particular instrument's features and operation.
    • A willingness to learn the details of the requests IC-CAP places on drivers, and the order in which they occur during principal operations: Measure, Calibrate, and Rebuild (instrument list).
    • A copy of the C++ language system provided by your computer vendor, including manuals and a license.

Driver Development Concepts

The basic form of user-added drivers involves 1 file with declarations of data types and functions, and 1 file with implementations of functions. Because Driver Generation Scripts are provided, very few modifications to the declarations file are necessary; work is largely confined to the function implementations file. The separation of declarations and implementations is common practice, and has been used with User C Functions. The source directory $ICCAP_ROOT/src is used for OMI compilation, just as it is for User C Functions.

The default source files for new drivers already contain example drivers:

    • HP 4194:  user_meas.hxx and user_meas.cxx
    • HP 4140:  user_meas2.hxx and user_meas2.cxx
    • HP 54510:  user_meas3.hxx and user_meas3.cxx

Unless you choose to add files to optimize your compilation process, the $ICCAP_ROOT/src/Makefile permits the make(1) command to create an up-to-date IC-CAP executable file with your latest modifications. This Makefile accounts for the distinct compilation needs of the C++ and C source files by invoking the appropriate compiler. By default, make(1) understands a .cxx suffix to mean C++ compilation, and .c to mean C compilation; the Open Measurement Interface follows this convention.

The process for building the shared libraries libicuserc.<ext> and libicusercxx.<ext> is demonstrated in the following figure. It is not necessary to know the details; the make(1) command can perform the entire process (provided the $ICCAP_ROOT/src/Makefile is correct).

The user driver files, user_meas.hxx and user_meas.cxx, to which your driver is added by default, already contain an example driver. This keeps the facility simple but could slow your compilation. If you choose to add your code to other files, adjust the Makefile. Otherwise, do not modify the Makefile.

Figure 5 Flow Diagram for the User Build Process


Note


The pbench.o file is supplied since it is required to build the shared library. However, the source is not provided, so you cannot modify it.

Additional information is available online in example drivers, header files, and comments inside the code generated by the driver generation scripts.


Example Drivers    Three example drivers HP 4194, HP 4140, and HP 54510 can be seen in the Instrument Library in the Hardware Setup window.

    • Source files for the HP 4194 are user_meas.hxx and user_meas.cxx
    • Source files for the HP 4140 are user_meas2.hxx and user_meas2.cxx
    • Source files for the HP 54510 are user_meas3.hxx and user_meas3.cxx

The information provided by these example drivers should serve as valuable reference material for adding a new driver.

Header Files    Files that are normally modified and re-compiled, user_meas.hxx and user_meas.cxx, use include (or header) files. The most important header files are unit.hxx, user_unit.hxx, instr.hxx, and user_instr.hxx. These files declare all of the virtual functions for each driver, and provide information to write (or avoid writing) each function.

Generated Code and Comments    The driver generation scripts generate both code and comments. Generally, the comments state what each required function must return, when it is invoked, and its purpose. Code examples are often provided that you can use as the basis for the code you must provide. To access this information, run the scripts. For information, refer to Driver Generation Scripts.

Binary Byte Order

For information on transferring binary data between an instrument and IC-CAP, see the README.byteorder file in the source directory $ICCAP_ROOT/src. It contains important information with respect to the order of bytes in a multi-byte number.

Adding a Driver

The basic steps (details are provided in the paragraphs that follow) for adding a driver are:

  1   Run the Driver Generation Scripts.

  2   Fill in functions that control your instrument.

  3   Inform IC-CAP of the new instrument type.

  4   Build the IC-CAP executable file.

  5   Debug the new driver.

Driver Generation Scripts

The driver generation scripts provide a framework of functions into which a user's driver code is placed.

mk_unit    This script generates code for units in an instrument. In the case of an HP 4141, for example, there are 8 units, including 4 DC SMUs, 2 VS and 2 VM units. The HP 4194 example has just 1 unit, which is typical for a CV driver.

A transcript of the mk_unit session used for the HP 4194 driver is as follows:

 $ mk_unit
Enter a name for the unit class for which you want code:
cvu_4194 
Enter a name for the instrument class that will use this unit
class: hp4194 
Enter the full name of the .hxx file that will declare hp4194
default: user_meas.hxx]:
Enter a name of twelve characters or less; the emitted code
will be appended to .cxx and .hxx files with this basename
[default: user_meas]:
Done. C++ code was added to user_meas.hxx and user_meas.cxx.
You should re-run mk_unit if more unit types are needed.
Otherwise, you probably need to run mk_instr now.

You must supply the name of 2 C++ classes. A class is a name for a user-defined C++ type and is like a struct in C. The mk_unit script uses your chosen class names throughout the generated code. In this example a unit class name (cvu_4194) was chosen to denote CV Unit in a 4194, and an instrument class name (hp4194) was chosen to reflect the name of the instrument. Try to select class names in the same style. Class names should be meaningful and specific, since this helps to avoid name collisions during compilation. Use, for example, a suffix relating to the instrument or company. Do not hesitate to take advantage of the fact that the C and C++ compilers generally accept very long names. The use of long descriptive names helps prevent compilation or linking problems due to name collisions.

If the instrument has more than 1 kind of unit to drive, like the HP 4141, run mk_unit repeatedly. If it has several identical units, do not re-run mk_unit. Identical units can be taken into account after running mk_instr.

mk_instr    This script generates code for instrument-wide functionality in a driver, such as calibration, self-test, and getting the instrument recognized during Rebuild (instrument list).

A transcript of the mk_instr session used for the HP 4194 driver is:

 $ mk_instr
Enter the name of the instrument class for which you want
code: hp4194 
Enter a name of twelve characters or less; the emitted code
will be appended to .cxx and .hxx files with this basename
[default: user_meas]: 
Done. C++ code was added to user_meas.hxx and user_meas.cxx.
Now you can go take a look at user_meas.cxx, and start doing
the real work. 
NOTE: in user_meas.cxx you may eventually need to add
#include statements to ensure that user_meas.cxx sees the
class declarations of any unit classes used by hp4194.
Disregard this if the necessary unit declarations appear at
the beginning of user_meas.hxx.(The mk_unit script should
generally have put them there.) 
You WILL need to declare some units in the class declaration
of hp4194 in user_meas.hxx (see comments therein).
After running this script, you generally need to run
mk_instr_ui next. 

This script requires the class name hp4194 to be repeated again, exactly as it was entered in mk_unit. (In your own driver, use another class name besides hp4194, but repeat the same instrument class name when each script asks for it.)

The script mentions the need to declare some units, which is accomplished by manual edits to the user_meas.hxx file; for example

 cvu_4194* cv_unit ; 

accomplishes that for the HP 4194 driver in the file user_meas.hxx file. If the HP 4194 had 2 identical CV units available, this declaration might have been

 cvu_4194* cv_unit_1 ;
cvu_4194* cv_unit_2 ; 

mk_instr_ui    This script generates code that fully implements the Instrument Options tables appearing in Setups that use the instrument driver. Within these tables, an IC-CAP operator can specify such things as Delay Time, Integration Time, and other instrument-specific options. Because this script completely writes out the necessary C++ code for this user interface functionality, it asks more questions than the previous scripts.

A transcript of the mk_instr_ui session used for the HP 4194 driver is:

 $ mk_instr_ui
NOTE: valid types for editor fields are these:
{ real | int | char | boolean | string }
Enter the name of the instrument class for which you want UI
   code: hp4194
Enter a name of twelve characters or less; the emitted code
   will be appended to .cxx and .hxx files with this basename
   [default: user_meas]: 
Enter the label for an editor field (or enter a null string
   if no more fields are desired): Use User Sweep
Enter a type for editor field 'Use User Sweep' [h for help] :
   boolean
Enter an initial value for this field [ 0 or 1 ] : 0 
Enter the label for an editor field (or enter a null string
   if no more fields are desired): Hold Time
Enter a type for editor field "Hold Time" [h for help] : real
Enter the minimum legal value for this field: 0
Enter the maximum legal value for this field: HUGE
Enter a granularity value (for rounding this field; 0 for no
   rounding): 0
Enter an initial value for this field: 0 
Enter the label for an editor field (or enter a null string
   if no more fields are desired): Delay Time
Enter a type for editor field "Delay Time" [h for help] :
   real
Enter the minimum legal value for this field: 0
Enter the maximum legal value for this field: 3600
Enter a granularity value (for rounding this field; 0 for no
   rounding): 0
Enter an initial value for this field: 0 
Enter the label for an editor field (or enter a null string
   if no more fields are desired): Meas Freq 
Enter a type for editor field "Meas Freq" [h for help] :
Sorry, "" is not a valid type.
The valid types are: { real | int | char | boolean | string } 
Enter a type for editor field "Meas Freq" [h for help] : real
Enter the minimum legal value for this field: 100
Enter the maximum legal value for this field: 100e6
Enter a granularity value (for rounding this field; 0 for no
   rounding): 1
Enter an initial value for this field: 1e6 
Enter the label for an editor field (or enter a null string
   if no more fields are desired): Integ Time
Enter a type for editor field "Integ Time" [h for help] :
   char 
This field will force the user to enter one character, from
   within a set of valid characters you will specify now.
Example set of valid characters: TFYN
Enter the set of character values that this field can take
   on: SML
Enter whether this field should force user input to
   uppercase [y/n]: y
Enter an initial value for this field: S 
Enter the label for an editor field (or enter a null string
   if no more fields are desired): Osc Level [.01-1Vrms]
Enter a type for editor field "Osc Level [.01-1Vrms]" 
   [h for help] : real
Enter the minimum legal value for this field: .01
Enter the maximum legal value for this field: 1 
Enter a granularity value (for rounding this field; 0 for no
rounding): 0 Enter an initial value for this field: .01 
Enter the label for an editor field (or enter a null string
   if no more fields are desired): Averages [1-256]
Enter a type for editor field "Averages [1-256]" [h for help]
   : int
Enter the minimum legal value for this field: 1
Enter the maximum legal value for this field: 256
Enter an initial value for this field: 1 
Enter the label for an editor field (or enter a null string
   if no more fields are desired): Delay for Timeouts
Enter a type for editor field "Delay for Timeouts" 
   [h for help] : real
Enter the minimum legal value for this field: 0
Enter the maximum legal value for this field: HUGE
Enter a granularity value (for rounding this field; 0 for no
   rounding): 0
Enter an initial value for this field: 0 
Enter the label for an editor field (or enter a null string
   if no more fields are desired): 
Done. All necessary C++ UI code was added to user_meas.hxx
   and user_meas.cxx.

From the nature of the questions in this script, this process defines an editor table for the instrument. The table offers some advanced features, such as constraining the type and the range of values that an operator can enter in each field.

Running the Scripts on Windows

To run the mk_instr, mk_unit, and mk_instr_ui scripts on Windows, first edit the file $ICCAP_ROOT/bin/icrun.bat. You must set ICCAP_ROOT accordingly by modifying the line:

SET ICCAP_ROOT=<Path to IC-CAP>

for example if you installed IC-CAP at C:\Agilent\ICCAP_2006B, edit the file to read

SET ICCAP_ROOT=C:\Agilent\ICCAP_2006B

Once this is set, simply change <name> below to mk_instr, mk_unit, or mk_instr_ui to run those scripts.

icrun <name>

Running the Scripts on UNIX

This section contains information about running the scripts, questions asked by the script, and the form of user responses.

    • They are invoked as UNIX commands. Execute cd $ICCAP_ROOT/src unless you just want to experiment with the scripts in another directory like /tmp. The cd command ensures that the code goes where the Makefile expects.
    • Make backup copies of user_meas.hxx and user_meas.cxx before starting to use the scripts.
    • The scripts are run in order: mk_unit, mk_instr, and mk_instr_ui. Running the scripts out of order may cause compilation errors when the compiler encounters types, classes, or variables before they are properly declared.
    • All the scripts prompt you with a series of questions. The effect of the scripts is to write C++ code onto the end of the user_meas.hxx header file and the user_meas.cxx implementation file.
Plan your response by reviewing the transcripts and comments presented previously for each script to avoid re-running the script. Multiple passes by the scripts could put declarations into user_meas.hxx more than once causing error messages from the scripts or a compile-time message such as error 1113: class <some_class_name> defined twice. To re-run the script, restore user_meas.hxx and user_meas.cxx to the same state as they appear on the IC-CAP product media.

    • When providing real number values to mk_instr_ui supply values that a C compiler will accept. The engineering notation accepted by IC-CAP's PEL interpreter, such as 15meg, or 2k, is not accepted by the compiler. Examples of acceptable real numbers are:
1.0
10.5e6
HUGE (a constant from /usr/include/math.h)

    • granularity as used in real fields, refers to a flexible rounding feature. For example, if your instrument has an option Osc Level for which the instrument has only 10 mV resolution, enter granularity as 10e-3. The Instrument Options editor then protects the IC-CAP operator from entering values the instrument can't support.

The scripts require you to fill in functions in user_meas.cxx. They also require a few minor adjustments in user_meas.hxx. These adjustments are:

    • The instrument class should declare any units owned by the instrument. This is discussed under mk_instr.
    • You may encounter compilation errors when unit and instrument functions attempt access to each other's data members, since this violates normal C++ access rules. For example, in user_meas.cxx, in hp4194::init_instr(), a function of the hp4194 class accesses a data member of the cvu_4194 class with this statement: cv_unit -> oscillator_on = 1 ;
A typical compiler error message would be:

error 1299: init_instr() cannot access
cvu_4194::oscillator_on:
private member
One way to remedy this is to let the unit and instrument class declare each other as friends. For example, the declarations

friend class cvu_4194 ; 
and

friend class hp4194 ; 
in user_meas.hxx, permit the hp4194 functions to access the cvu_4194 data members, and vice-versa.

Filling in Necessary Functions

After running the scripts, you must write the body portions of the functions added to user_meas.cxx. This section provides hints to help you accomplish this.

For help filling in a function body, look at the declarations and functions generated by the scripts. These provide comments explaining the purpose, return value, and invocation time of each function.

Next look at the declarations and functions of the HP 4194 example driver. This section contains examples of code accomplishing required tasks. The following manual sections may also be helpful.

    • Programming with C++
    • Order in Which User-Supplied Functions are Called. Provides useful information about the sequence in which functions are invoked. Decisions must often be made about which function should perform particular instrument manipulations; these decisions can be aided by seeing when each function runs.
    • What Makes up an IC-CAP Driver. Explains the functionality expected in areas such as Calibration and Hardware Setup Operations. The functions whose bodies you need to write are grouped in that section by functional category.

You may want to proceed in stages. For example, start with Hardware Setup Operations to demonstrate that Rebuild (instrument list) can find the instrument and display the driver and instrument in the Hardware window. Then implement the functions that support Measure. Address those functions that support Calibrate, if desired. During the time your driver is partially implemented, compiler warnings serve as a rough indication of functions not yet implemented.

The GPIB analyzer (Tools menu), and especially its macro features (described elsewhere in this chapter), are helpful when developing the appropriate sequence of commands to use with the instrument.

Making a New Instrument Type Known to IC-CAP

Running the mk_instr script makes a new instrument type known to IC-CAP. The code involves an add_user_driver() function call, placed in user_meas.cxx by the mk_instr script.

Creating a New Shared Library

After any series of edits to the source files, you must generate 1 or 2 new shared libraries to pick up the modified files. The shared library names are libicuserc.<ext> and libicusercxx.<ext> where ext is a platform-specific extension. Use the extension .so for Solaris. The library libicuserc.<ext> holds C code and is used to add user C functions. The library libicusercxx.<ext> holds C++ code and is used to add instruments. The default location of these files on SUN Solaris 2.X is $ICCAP_ROOT/lib/sun2x. When you issue the make command, you will create a local version of the same file that includes your modifications. By setting an environment variable, you can direct IC-CAP to use your new shared library instead of the default library.

To generate the new shared library:

  1   Create a work directory for the source files (for example, mkdir my_source, and change it to (cd my_source).

  2   Copy the set of source files from $ICCAP_ROOT/src to the new work directory (cp $ICCAP_ROOT/src/* . ).

  3   Use the touch command on the *.o files so that all *.c and *.o files appear to have been created at the same time (touch *.o). (This step is important for the make procedure.)


Note


If the drive you're copying to is NFS mounted, clock skews can result if the NFS drive's system has a slightly different system time than the local system. If you think this might apply to you, first, execute touch * then execute touch *.o. The first touch synchronizes all files to your local system's time; the following touch causes the make system to believe that all of the .o files were generated later than the source files, so it will not attempt to rebuild any unnecessary files.


  4   Copy your source code to the working directory. Modify the function add_users_c_funcs() in userc.c to add your C functions to IC-CAP's list of functions, and/or modify the function add_users_drivers() in user_meas.cxx to add your drivers to IC-CAP's library of instrument drivers. Modify the Makefile to add your source code modules to the list of objects.

  5   Issue the make command and debug any compiler errors.

  6   Set the environment variable ICCAP_OPEN_DIR to point to the directory containing the libicuserc.<ext> or libicusercxx.<ext> file where ext is a platform-specific file extension (ext is .so on Solaris).

Alternately, if you want to use the new files site wide, you can replace the original files (after copying to another name to preserve them) under $ICCAP_ROOT/lib/<platform>.

  7   Start IC-CAP as usual.

Troubleshooting Compiler Errors

The definitive authority on compiler errors is your compiler documentation. This section offers assistance with some of the common messages you may encounter when compiling OMI drivers.

The message

 CC: "user_meas.cxx", line 899: warning: outptr not used (117) 

usually indicates that you have not yet filled in a function, with the result that the function is not using all of its arguments. In some cases the function may not use all of its arguments, so the message may not be important.

Resolution of the message

 error 1299: some_unit_func cannot access
some_instr_class_name::some_member: private member 

is discussed in Running the Scripts on Windows.

The message

 CC: "user_meas.hxx", line 9: error: class x defined twice (1113)

indicates that the Driver Generation Scripts were probably run twice.

For help, refer to Running the Scripts on Windows.

Debugging

This section provides information about debugging driver code, after iccap.new has been compiled, including the xdb debugger and GPIB analyzer (Tools menu).

Using the xdb Debugger

The default Makefile arranges for debug information to be available after linking the executable file. This is done with the -g flag among the CFLAGS in the Makefile.

The debugger commands described in the following table should be tried in the order presented.

Table 50 Debugger commands for the xdb debugger
Command
Action
cd $ICCAP_ROOT/src
Changes directories. Debugging works best when the current directory contains the source files and the binary.
xdb iccap.new
Starts the debugger.
z 8 isr; z 16 isr ; z 18 isr
Tells the debugger not to interfere with 3 signals managed by other parts of iccap.new.
r bjt_npn.mdl
Runs iccap.new, specifying bjt_npn.mdl as a command line argument. If the debugger stops with a message such as bad access to child process, ignore it and enter c to continue.
BREAK or CTRL-C
Suspends iccap.new, to give further debugger commands. xdb does not execute commands unless iccap.new is suspended.
v user_meas.cxx
Enables you to view and edit a source file. This command is helpful for setting breakpoints.
td
Toggle display. Toggles the display mode between assembly code and C/C++. Use this if the preceding command displays assembly code on the screen, or if no code is displayed.
/hp4194::find
Searches forward (as in vi) to view the source for the function hp4194::find_instr().
v nnn
Enables you to view line nnn at the center of the screen where nnn is the line number you want to view.
b
Sets a breakpoint at the line currently centered on the screen. Sometimes the debugger chooses another nearby line, especially if the currently centered line is blank, or is only a declaration statement. When iccap.new resumes running, the debugger stops iccap.new whenever this line of code is about to be executed. You may set several breakpoints.
b nnn
Similar to the last command. Sets a breakpoint at line nnn where nnn is the line number you specify. Sometimes the debugger chooses another nearby line, especially if you chose a blank line, or a line with only a declaration statement.
S
Big step. Steps through 1 line of source code without stopping inside any procedure calls encountered.
Little
Like S, but this stops inside any debuggable procedure that is encountered while executing the line of code.
c
Continues execution of iccap.new.
Execute a menu function
To reach breakpoints in the driver code, use Measure, Calibrate, or Rebuild as appropriate. For help in making this choice, refer to Order in Which User-Supplied Functions are Called. Be sure that the function will actually be called if you want the breakpoint reached.
p address
When the debugger hits a breakpoint in a procedure, this command prints the value of an argument passed to the procedure, or a local variable in the procedure. In this example, the argument/variable is named address.
p address=23
To assign a new value to an integer variable named address, employ this special form of the p (print) command.
p *this\K
Prints the member data of the C++ object in whose member function the current breakpoint is located.

GPIB Analyzer (Tools menu) and IC-CAP Diagnostics

In addition to xdb, debugging capabilities are built into IC-CAP.

The GPIB analyzer (Tools menu) in the Hardware Setup window includes the following features.

    • The I-O Screen Debug On menu selection can monitor all activity on the GPIB bus. Observe the GPIB commands and responses associated with your driver, as well as other IC-CAP drivers.
    • The analyzer can be used for interactive I/O activities, to force an instrument state, poll the instrument, or test the effect of a command.
    • Analyzer operations can be collected into a file for macros for rapidly prototyping the GPIB commands to be used in a driver. For more information about macro files of this sort, refer to GPIB Analyzer.

The generation of IC-CAP diagnostic messages can be activated by menu functions under Tools in the IC-CAP Main window.

Alternatives to Creating New Drivers

If you don't need an instrument driver to be as fully integrated as HP/Agilent-provided drivers, it may be worthwhile to consider controlling the instrument by means less formal than creating a driver using the Open Measurement Interface.


Note


There is an important shortcoming with these suggestions. An IC-CAP measurement currently provides no mechanism for Program Transforms or Macros to be invoked at critical times in the interior of the measurement (for example, at the instant when DC bias levels have just been established by SMUs, and it is time for a main sweep instrument to stimulate the DUT and collect data). Use of the Open Measurement Interface overcomes such limitations.


    • Use the PRINT statement in an IC-CAP Macro to direct commands to an instrument, when a suitable device file has been established using the mknod command.
    • Use the functions listed with USERC_write and USERC_read in a Program Transform or Macro to provide limited instrument control. For descriptions of the User C functions in general, refer to Chapter 8, "IC-CAP Functions." For details and examples of the input/output functions, refer to Appendix H, "User C Functions."
    • Rather than using the Measure menu selection directly, construct Macros in the following style to enclose the measurement between operations controlling other hardware:
 ! Steps 1, 2, and 3 are assumed to be implemented by PRINT.
! 1) Force next desired set point on temperature chamber.
! 2) Enable waveform generator.
iccap_func("/opamp/time_domain/positive_slew","Measure")
! 3) Disable waveform generator.
! One way to control the values desired for temperature and
! frequency is to access IC-CAP system variables.

What Makes up an IC-CAP Driver

In addition to measurement capabilities, each IC-CAP driver possesses other capabilities, such as the user interface functionality provided in Instrument Options folder and the ability to participate in Input, Output and Setup Checking prior to measuring. Each of these essential areas is discussed in this section. In each area, information is provided about the specific functions necessary to complete that part of a driver.

In the tables throughout this section, the prefix unit:: means the class name(s) you provided for units when you ran the mk_unit script. The prefix instr:: should be considered to mean the class name you provided for the instrument when you ran the mk_instr script. The column Importance indicates whether you typically need to write any code for the function. Because of the inheritance features of C++, you must often rely on inherited default functions. Functions important to write, typical return values, and other information can be determined from the comments for the function in $ICCAP_ROOT/src/user_meas.cxx.

Instrument Options

The Instrument Options folder provides a method for selecting certain instrument conditions for a measurement. Certain instrument conditions are separated into different groups of instrument options (rather than appearing in Input sweep editors) because they are highly instrument specific, and play no role in simulation. The options displayed in the Instrument Options folder typically vary with each setup that participates in the measurement involving a particular instrument.

The Driver Generation Scripts, described in Procedure for Adding a Driver, can write all the C++ code that is necessary to establish appropriate instrument options tables for a new driver. The driver generation script named mk_instr_ui prompts for the desired contents of the instrument options tables, after which it proceeds to generate the necessary declarations and implementations in C++. The generated code will contain data structures in which options are stored, as well as the user interface linkages that display the options for editing.

Input, Output and Setup Checking

When you initiate Measure or Calibrate for a Setup, IC-CAP first verifies the validity of the measurement Setup. This permits many operator errors to be detected and reported before IC-CAP undertakes instrument I/O.

IC-CAP performs the following 3 kinds of checks:

    • Checks Input (Sweep) specifications; for example, does a Start or Stop value exceed the instrument's range?
    • Checks Output specifications; for example, can the instrument measure the type of data desired, such as capacitance?
    • Checks overall Setup structure; for example, is there more than 1 time or frequency sweep being requested?

The following table describes the functions related to input (sweep) checking.

Table 52 shows a summary of the supported Input (Sweep) modes in IC-CAP. The column Character Used in Driver Functions shows the character passed when an Input Mode is passed to a function, such as unit::can_source.

Table 53 describes the functions related to output checking.

Table 54 shows a summary of the supported Output modes in IC-CAP. The column Character Used in Driver Functions shows the character passed when an Output Mode is passed to a function, such as unit::can_measure.

Table 51 Functions for Input Checking 
Function Name
Purpose
Importance
instr::use_second_sweep
tells if unit has 2 internal sweeps
default usually OK
unit::can_source
tells if unit can source a given Mode
important
unit::can_source_vs_time
tells if unit can source time-domain signals
important for pulse generators
unit::check_bias_swp
reserved for future use
default is OK
unit::check_sweep
lets unit check/preview Input data set
important
unit::check_sync
checks sync sweep spec.
important if implementing sync sweeps

Table 52 IC-CAP Input (Sweep) Modes
Character Used in Driver Functions
Meaning
V
Voltage
I
Current
F
Frequency
T
Time
P
Parameter
U
User (refer to User-Defined Input and Output Modes)

Table 53 Functions for Output Checking 
Function Name
Purpose
Importance
unit::can_measure
tells if unit can measure a given Mode
important
unit::can_measure_vs_time
tells if unit can measure time-domain signals
important for oscilloscopes
unit::check_out
lets unit check/preview Output data set
important if measures multiple data sets

Table 54 IC-CAP Output Modes
Character Used in Driver Functions
Meaning
V
Voltage
I
Current
C
Capacitance
G
Conductance
T
Time Domain Pulse Parameter (like RISETIME)
S, H, Z, Y, K, A
Two-Port
U
User (refer to User-Defined Input and Output Modes)

Setup checking is performed primarily by logic embedded in IC-CAP. A limited amount of the checking is accomplished with user-supplied functions. The following table describes the user functions related to overall Setup checking.

Table 55 Functions for Overall Setup Checking 
Function Name
Purpose
Importance
instr::find_instr
checks GPIB for instrument
necessary
instr::find_units
locates optional units
default usually OK
instr::set_found
remembers instrument was found; could set internal flags concerning presence of optional hardware modules
default usually OK
instr::use_second_sweep
tells if unit has 2 internal sweeps
default usually OK
unit::bias_compatible
checks if this unit can tolerate signal or bias from another unit
could potentially save fuses.
unit::can_do_second_sweep
tells if another sweep and unit can be an internal sweep secondary to the sweep for this unit
default is OK

User-Defined Input and Output Modes

Mode U is a reserved user-defined mode that allows some flexibility for safely checking any new signal modes to be sourced or measured. This feature is for situations where it is not practical or safe to use existing Input or Output modes (such as voltage or capacitance).

The following considerations apply:

    • Units associated with existing drivers are likely to reject U. For example, a HP 4141 VM unit will not force or measure U. In such a case the measurement is disallowed. (It does not make sense for the IC-CAP HP 4141 driver to try to force or measure U-Mode data, since it does not know what U-Mode means.)
    • The unit functions associated with the new driver can enforce any desired policy for a U-mode Input or Output, as well as the other Input and Output Modes.
    • With a U-Mode Input or Output in an IC-CAP Setup, do not expect the Simulate menu function to work on that Setup.

Calibration

Calibration functions are associated with the instrument, not its units. To perform calibration procedures initiated from the IC-CAP program, implement the functions shown in the following table.

Table 56 Functions for Calibration
Function Name
Purpose
Importance
cal_possible
tells if the other 2 functions do anything
These functions are necessary if IC-CAP is to calibrate the instrument.
do_cal
downloads Setup, leads operator through calibration procedure

recall_n_chk_calib
activates calibration during Measure; checks sweep

Several of the functions required for Measure are also used during Calibrate. Refer to Order in Which User-Supplied Functions are Called in this chapter for a list of functions called during Calibrate.

Storage is provided in the instr_options class for limited calibration data for a particular instrument in a particular Setup. The instr_options class is declared in instr.hxx.

The data members in instr_options for holding calibration results are:

    • String cal_data ; //
declare your own data if String is not an appropriate type
    • calib_status last_cal_status ; //
calib_status is an enumeration with these possible values:
     CAL_OK
     CAL_ERROR
     CAL_ABORTED
     CAL_NEVER_DONE

Set calib_status during do_cal() and test it during recall_n_chk_calib(). Recall that cal_possible() and do_cal() are invoked (in that order) during Calibrate, while recall_n_chk_calib() is later called during Measure, with the purpose of enabling the desired calibration set.

Derived from the class instr_options (declared in instr.hxx) is user_instr_options, declared in user_instr.hxx. For the new driver, a further derived class will have been declared in user_meas.hxx by the mk_instr_ui script. The section Class Hierarchy for User-Contributed Drivers clarifies the relationships of these classes.

The class in user_meas.hxx that is derived from user_instr_options is an appropriate place to declare additional calibration data the workstation should retain, because a distinct object (or data structure) of this type exists in every situation where distinct instrument calibration data might be needed. In other words, an instrument has a distinct user_instr_options object in every Setup where the instrument is used. For the example of the HP 4194 driver, such data (if any) would be declared in the class named hp4194_table in user_meas.hxx. You might declare several double numbers, to keep a record of sweep limits that were in effect at the time of Calibrate, so that they can be verified during Measurement. (With many instruments, calibration is not valid unless measurements employ the same sweep limits that were in effect during calibration.)


Note


To simplify an initial pass at implementing calibration, do not declare additional data structures for remembering sweep parameters, and do not perform much verification during recall_n_chk_calib().


If you choose to declare additional calibration-related data in the class derived from user_instr_options, it is possible for this data to be archived and re-loaded with IC-CAP Model(.mdl), DUT(.dut), and Setup(.set) files. Note that the archiving of user-defined calibration data is an advanced feature that most implementations can probably avoid considering.

To archive user-defined calibration data, your class derived from user_instr_options must redeclare and implement 2 virtual functions. These functions are read_from_file and write_from_file, declared for the class instr_options, in the file instr.hxx. When called, these functions receive an open stdio FILE*, which provides read or write access to the IC-CAP archive file at the appropriate time during a Read From File or Write to File menu function.

Measurement: Initialization, Control and Data Acquisition

The functions in this area perform the real work of the instrument driver; this area accounts for the largest number of functions present in each driver.

Initialization functions are listed in the following table.

Table 57 Initialization Functions 
Function Name
Purpose
Importance
instr::init_instr
downloads information from the Instrument Option Table
necessary
instr::reset_instr_info
clears flags in driver, refer to instr.hxx
default usually OK
instr::reset_outptrs
nulls out output data set pointers, refer to instr.hxx
default usually OK
instr::zero_supplies
puts instrument to safe state, turns off sources
necessary
unit::enable_output
enables any output unit needing explicit enabling (refer to user_meas.cxx)
necessary with some instruments, such as the HP 4141
unit::init_unit
reserved for future use
default is OK
unit::reset_inassign
reserved for use by 4142 and 4145
default is OK
unit::reset_outassign
reserved for use by 4142 and 4145
default is OK
unit::set_2_internal_sweeps
downloads specifications for 2 nested internal sweeps
default usually OK
unit::set_internal_sweep
downloads specifications for internal sweep
necessary
unit::set_sync
downloads specifications for sync sweeps
default usually OK
unit::zero_supply
puts unit to safe state, suppresses bias and so on
necessary, if the unit can source bias or other signal

Control and data acquisition functions are shown in the following table.

Because many of the functions in this category must perform non-trivial work, such as instrument communication and error reporting, refer to Programming with C++, where such operations are explained. The examples for the cvu_4194 member functions and the hp4194 member functions in user_meas.cxx are also helpful.

A few of the functions in this area are provided for the support of a particular instrument, for example, the HP 4145. The intermediate classes user_unit and user_instr do not redeclare some of these low-usage functions, though their declarations are inherited from the unit and instr classes, so they could be used in a new driver if needed. For example, instr::use_second_sweep() is re-declared and used only by the HP 4145 driver.

Table 58 Control and Data Acquisition Functions 
Function Name
Purpose
Importance
instr::copy_outds
does delayed data set stuffing (refer to instr.hxx)
default usually OK
instr::fill_outds
similar to copy_outds (refer to instr.hxx)
default usually OK
instr::get_outptr
gives pointer to Output data set
default usually OK
instr::keep_mdata
keeps 1 data point
default is OK
instr::out_count
tells number of output pointers in instr class
default usually OK
unit::can_do_second_sweep
tells if 2 internal sweeps OK (refer to unit.hxx)
default is OK
unit::define_channel
reserved for 4145
default is OK
unit::enable_sync
reserved for future use
default is OK
unit::fill_outds
any data this unit has kept internally, or in data structures of the instrument or unit driver, that belong in outptr, should be saved there now (refer to user_meas.cxx)
default usually OK
unit::get_data
gets data from the instrument
(Refer to user_meas.cxx)

necessary
unit::get_int_bias
reserved for future use
default is OK
unit::get_scalar_data
reserved for 54120 series
default is OK
unit::list_chan_num
reserved for 4142
default is OK
unit::list_output_name
reserved for 4145
default is OK
unit::meas_err
used by some drivers to make error messages
default is OK
unit::set_bias
forces a bias value
necessary for user sweep
unit::set_data_out
reserved for 4145
default is OK
unit::set_scalar
reserved for 54120 series
default is OK
unit::source_const_unit
reserved for 4145
default is OK
unit::source_unit
reserved for 4145
default is OK
unit::trigger
directs a unit to perform the measurement specified earlier via set_internal_sweep
necessary
unit::turn_chan_OFF
reserved for 4145
default is OK
unit::wait_data_ready
allows the instrument to finish measurement before trying to get data after trigger. In the cvu_4194 code, this wait is accomplished in the trigger function.
default usually OK
unit::wait_delay_time
implements Delay Time prior to a spot measurement. Refer to cvu_4194 case in user_meas.cxx.
necessary
unit::wait_hold_time
implements Hold Time prior to a User Main Sweep. Refer to cvu_4194 case in user_meas.cxx.
necessary

Hardware Setup Operations

The Hardware Setup functions, listed in the following table, are used in the following operations:

    • Maintaining lists of instruments and units
     Adding and deleting instruments
     Maintaining the unit table, including the addition of entries due to newly added instruments
     The Rebuild (instrument list) function
    • Self-testing the instruments
    • Polling the instruments

Table 59 Hardware Setup Functions 
Function Name
Purpose
Importance
instr::addl_addr_label
reserved for 8510 and 8753
default is OK
instr::build_units
creates unit objects. Refer to hp4194::build_units in user_meas.cxx.
necessary
instr::find_instr
checks GPIB for instrument
necessary
instr::find_units
locates optional units
default usually OK
instr::get_addl_addr
reserved for 8510 and 8753
default is OK
instr::get_ID
gets instrument ID string. Refer to user_meas.cxx.
necessary
instr::get_unit_by_name
finds a unit in the instrument
default is OK
instr::instr *
initializes data members of instrument object
necessary
~instr::instr *
cleans up members of instrument object
necessary
instr::read_units
reserved for 4142
default is OK
instr::rebuild_units
reserved for 4142
default is OK
instr::set_found
remembers that instr found; may test and set internal flags concerning presence of optional hardware modules
default usually OK
instr::test_instr
supports the Run Self Tests menu function in the Hardware Setup window
default is OK (no self-test)
instr::unit_count
tells how many units the instrument has
necessary
instr::units_configurable
reserved for 4142
default is OK
instr::write_units
reserved for 4142
default is OK
unit::unit *
initializes data members of unit object
necessary
~unit::unit *
cleans up members of unit object
necessary

* denotes a constructor or destructor function for which the actual name is the unit or instr class name chosen when the mk_unit and mk_instr scripts were run. For example, hp4194::hp4194, in user_meas.cxx.

Programming with C++

This section provides examples of code for common Open Measurement Interface programming tasks.

    • Access to Inputs (Sweeps) and Outputs
    • Error and Warning Messages
    • Reading from an Instrument
    • Serial Poll of an Instrument
    • String Handling
    • Time Delay
    • User Input with a Dialog Box
    • Writing to an Instrument

Access to Inputs (Sweeps) and Outputs

In user_meas.cxx the function cvu_4194::check_sweep demonstrates how to determine sweep properties like Mode (V, for example), Type (LOG, for example), compliance, and start and stop values.

IC-CAP computes all necessary step values. Do not attempt to compute them from start, stop, and so on, because simulations will use the values IC-CAP computes. Instead, access individual sweep steps with the get_point function.

Following are statements from cvu_4194::check_sweep that determine sweep properties and get sweep values. These statements are isolated examples and are not necessarily to be used in the order shown.

 int cvu_4194::check_sweep(sweep* swp)
// header of the function used    here
sweep_def *swpdef = swp->get_sweep_def();
// a sweep uses sweep_def for values
switch(swpdef->get_esweep_type())
// to see if it's CON, LOG, LIN, ...
compval = swp->get_compliance();
// compliance 
case CON:
   val1 = ((con_sweep *)swpdef)->get_value();
// value of CON sweep
case LIN:
   val1 = ((lin_sweep *)swpdef)->get_start();
// start value of LIN sweep
   val2 = ((lin_sweep *)swpdef)->get_stop();
// stop value
((lin_sweep *)swpdef)->get_stepsize()
// step size 
// next 2 are taken from cvu_4194::set_internal_sweep: 
linswp = (lin_sweep*)swpdef ;
// to enable lin_sweep functions
numpoints = linswp -> get_num_points();
// number of points 
if (swp->get_sweep_order() == 1)
// sweep order; 1 => main sweep
switch (swp->get_mode())
// Mode: 'V', 'I', 'F', ...
swp->get_size()  // Number of points
swp->get_point(step_num)
// get one point (indexed from 0) 

The class named sweep is declared in sweep.hxx. Using a sweep often involves using functions it inherits from the class ds (data set), declared in ds.hxx. The function get_point is an example of a function inherited from ds. The sweep_type class is in sweep_type.hxx.

To save measured data to an IC-CAP Output data set, employ the style in cvu_4194::get_data:

 dsptr -> keep_point (index++, datapoint, DATA_MEAS); 
// datapoint is a double

In the example, dsptr points to a ds object. The class ds declares other forms of the keep_point function in ds.hxx. These can store complex or 2-port matrix data into the Output data set.

Error and Warning Messages

The IC-CAP error box appears after a measurement, displays one or more messages, and must be dismissed by clicking OK if you make one or more statements such as

 errbox << "ERROR: HP4194 unsupported internal sweep type."
<< EOL;
errbox << "ERROR: HP4194 sweep produced " << num_points_kept
<< "when" << swp_num_points << " were requested" << EOL ;

Warnings are displayed in the Status window:

 cerr << "WARNING: HP4194 frequency rounded up to 100Hz" << EOL ;

The objects errbox and cerr accept any number of arguments, of various types, including double, String, char*, int, and char. Separate them with << .

Reading from an Instrument

The user_meas.cxx file demonstrates 2 styles. Writing and reading are done with separate calls. In hp4194::get_id a readstring function is used as follows:

 stat=ioport->readstring(ad,id_buf,255);
// below is the code needed to call readstring from
// a unit class function
stat=get_io_port()->readstring(ad,id_buf,255);
// because the instrument owns and maintains the ioport 
// object, the unit gets it this way before using it 

The first argument above is the GPIB address. The id_buf argument is a buffer guaranteed to be adjusted by readstring to hold 255 bytes, if the read produces that many.

A function is also provided to write a query and then read an answer:

 if ( ioport->write_n_read(addr,"MKRB?", urbuf, 80) == -1 ) 

The first argument above is the GPIB address. The second argument is a char* to be written. The third argument is a buffer guaranteed to be adjusted by readstring to hold 80 bytes, if the read produces that many.

The above functions are 2 of many available for an hpib_io_port. Complete declarations of its functions are in io_port.hxx.

Serial Poll of an Instrument

The following functions are 2 of many available for an hpib_io_port. Complete declarations of its functions are in io_port.hxx.

Serial polling is done as follows:

 int status_byte = ioport->spoll(addr); 
// this example not from user_meas.cxx 
int status_byte = get_io_port()->spoll(addr); 
// call from a unit function 

To wait for a particular serial poll bit:

 // from cvu_4194::zero_supply:
hpib_io_port *ioport = get_ioport();
// bit-weight 1 below is to await 'measurement complete bit'
if ( ioport -> poll_wait(addr, 1, 0, 10.0) == -1 ) 

The arguments are: GPIB address, bit-weight to wait for, a flag reserved for future use, and maximum time that poll_wait should try (10 seconds).

String Handling

C++ offers a substantial improvement over C for handling String type data. In the file String.h a number of String functions are declared. The following code demonstrates several.

 String str_hello = "hello" ;                      // declare and initialize a string 
String str_world ;                                // just declare
str_world = "world" ;                             // assignment
String hello_world = str_hello + " " + str_world ;// concatenation
errbox << hello_world + "0 ;                      // writing to errbox
if ( "hello world" == hello_world )               // test for equality
String instr_cmd = "*RST" ;                       // initialize for next statement: 
if ioport->writestring(addr,instr_cmd) == -1 )    // String to instrument

In the final example, a char* is expected by writestring, and C++ automatically extracts it from the String. Do not pass a String to printf or scanf. The declarations of these functions in /usr/include/stdio.h use the ellipsis notation (...), so C++ does not know that a char* should be passed to them.

Time Delay

An example of a time delay is:

 delay ( 10E-3 ) ;   // 10 millisecond delay 

User Input with a Dialog Box

A number of functions for this purpose are declared in dialog.hxx. Examples to get data from dialog boxes are:

 // These use the versions of get_double and get_String that
//   each take 3 arguments.
double double_result ;
String String_result ;
int ok_or_cancel ; // 0 => OK pressed by user, and -1 => CANCEL
ok_or_cancel = get_double ("Give a double:",default_dbl_val,&double_result);
ok_or_cancel = get_String ("Give a string:",default_string,&String_result);

Writing to an Instrument

An example of writing to an instrument is:

 if ( ioport->writestring(addr,"TRIG") == -1 ) 
// cvu_4194::zero_supply 

The arguments are the GPIB address and a char* string to send. You can also write a query and read a response with 1 call, write_n_read, discussed in Reading from an Instrument. Writestring and write_n_read are 2 of many functions available for an hpib_io_port. Complete declarations of its functions are in io_port.hxx.

Syntax

This section provides help with reading the IC-CAP source code in user_meas.hxx, user_meas.cxx, and the various include files. Follow the example code in user_meas.hxx and user_meas.cxx when implementing a new driver.


Note


For best results when using the vi editor to browse the source files, execute the command :set tabstop=3


The C++ language introduces several keywords to help understand OMI programming, for example, class, new, delete, and virtual. Terms that are peculiar to OMI programming, for example, Measurer, sweep type, sweep order, main sweep, internal sweep, user sweep, unit function, and instrument data, are used in this chapter and in the source files.

Function declarations in C++ use the improved function prototypes of ANSI/C. For example,

 int mult_by_2(int input); 
// style for forward declaration int y=2 ;
int y = 2 ;
int x = mult_by_2(y) ; // example of invocation
int mult_by_2(int input)
// style for implementation (SAME AS DECLARATION)
{
   return 2*input ;
} 

This is an area of incompatibility with original (Kernighan and Ritchie) C. However, it is easier to read, and write, and is the emerging new standard. It also gives the compiler information with which function call argument lists can be checked, saving run-time aggravation.

Sometimes in class declarations you will see the function body present:

 const char *class_name() 
// this code from user_meas.hxx
{ return "cvu_4194" ; } 

These cases are called inline functions. They behave like normal functions, but the C++ compiler emits code inline, without normal function call overhead. For short functions this reduces both execution time and code size.

New Symbols and Operators

This section defines new symbols and operators in C++.

//     A pair of slashes introduces an end-of-line comment. (  /*  and  */  can still be used for C-style comments.)

&    Appearing after a type name or class name,  &  usually indicates that an argument to a function is passed by reference. Although C can pass arguments by address, the C++ notion of reference arguments eliminates many error-prone uses of  *  (pointer de-reference) and  &  (address) operators used with pointer handling in C. In the following example, the called function increments the callers variable:

 // 'input' passed by reference:
void increment(int& input) {
   input++ ;   // need not use *input
}
int x=3;
increment(x); // Need not pass &x
// Now x is equal to 4 

object. member_function( )    In C, the  .  operator is used to access data members in a struct object. In C++,   .   is also used to access (execute) function members.

ptr_to_object->member_function()    In C, the  ->  operator is used to access data members in a struct object to which one holds a pointer. In C++,  ->  is also used to access (execute) function members of a class type object to which you hold a pointer.


prevnext