EPICS Drivers for Canberra AIM and ICB Devices

Mark Rivers

Contents

Overview

The Canberra 556 AIM is an Ethernet multichannel analyzer in a NIM form factor. It has 2 ADC inputs, and a total of about 64K channels, each 32-bits deep. It can control programmable devices such as amplifiers, ADCs, high-voltage power supplies, etc. using an auxilliary bus called the Instrument Control Bus (ICB).

The AIM uses the IEEE 802.2 Extended SNAP protocol, which is a low-level protocol, and does not use TCP/IP. The protocol is not routeable, so the AIM must be placed on the same physical subnet as the IOC that is controlling it.

The AIM has a concept of module "ownership", i.e. a particular computer that can control it. Once an AIM is "owned" only that one computer can control the AIM, all other computers are prevented from accessing it. When the AIM is first powered on it is unowned, and any computer can take ownership. The EPICS driver attempts to gain ownership during the AIMConfig command. If it cannot, then it prints an error message. If the AIM is currently owned by another computer and you want to change what computer owns it, you can do one of two things. Either power off the AIM and back on, or issue the command mcaAIMFreeModule, described below.

The DSA-2000 is functionally the same as the AIM, but with a single ADC port and packaged in a rack-mount unit. The DSA-2000 contains the equivalent of the 9660 DSP which is set at ICB address 0. The DSA-2000 also contains an internal programmable high-voltage power supply. This is not the same as the ICB power supplies, and has its own driver described below.

The AIM and ICB device support runs on vxWorks, Linux, and Windows (win32-x86, windows-x64). It should also build and run on other Unix systems, including Mac OS X, as long as the libnet and libpcap libraries are available and installed. Note that Linux often comes with libpcap installed, but libnet often needs to be obtained separately.

The device support for the Canberra hardware uses the following:

AIM MCA setup

There is a separate asyn server for each ADC input port on an AIM. Each AIM can thus have two asyn ports associated with it. Each port card can have multiple "addresses", where each address is a region of AIM memory associated with a physical signal input. Each input signal can have more than one "sequence" or time-slice allocated to it. The configuration of an AIM input with an asyn port, and the allocation of memory within the AIM for this input is done by routine AIMConfig. AIMConfig is called as follows:

# AIMConfig(portName, ethernet_address, portNumber(1 or 2), maxChans,
#           maxSignals, maxSequences, ethernetDevice)

The total amount of memory (in channels) allocated for the port will be maxChans*maxSignals*maxSequences. The total amount of memory in the AIM is about 64,000 channels, and this is shared by the two ports.

When creating an MCA record which uses the AIM device support the INP field must be specified in the form:

    @asyn(portName address),NCHAN=nchans

where:

Example MCA startup script

Assume we have an AIM with Ethernet address 0x59e. To port 1 of this AIM there is a single ADC which is set for 2048 channels. This ADC will be used for "conventional" data acquisition, i.e. with no multiplexor and not doing time-resolved work. To port 2 of this AIM there is a multiplexor. The switches in the multiplexor have been set for 4 inputs and 4096 channels. The records for these multiplexor inputs only use 1024 channels in this example.

The system has a second AIM at Ethernet address 0x100. To port 1 of this AIM there is a single ADC which is set for 1024 channels. This ADC will be used for time-resolved work, and we want to collect 20 spectra within the AIM. To port 2 of this AIM there is an MCS which has front panel switches set to 2 inputs and 8192 channels.

AIMConfig("AIM1/1", 0x59e, 1, 2048, 1, 1, "dc0")
AIMConfig("AIM1/2", 0x59e, 2, 4096, 4, 1, "dc0")
AIMConfig("AIM2/1", 0x100, 1, 1024, 1,20, "dc0")
AIMConfig("AIM2/2", 0x100, 2, 8192, 2, 1, "dc0")

dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=mcaTest:,M=aim1_adc1,DTYP=asynMCA,INP=@asyn(AIM1/1 0),NCHAN=2048")

dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=mcaTest:,M=aim1_adc2_1,DTYP=asynMCA,INP=@asyn(AIM1/2 0),NCHAN=1024")
dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=mcaTest:,M=aim1_adc2_2,DTYP=asynMCA,INP=@asyn(AIM1/2 1),NCHAN=1024")
dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=mcaTest:,M=aim1_adc2_2,DTYP=asynMCA,INP=@asyn(AIM1/2 2),NCHAN=1024")
dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=mcaTest:,M=aim1_adc2_3,DTYP=asynMCA,INP=@asyn(AIM1/2 3),NCHAN=1024")

dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=mcaTest:,M=aim2_adc1,DTYP=asynMCA,INP=@asyn(AIM2/1 0),NCHAN=1024")

dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=mcaTest:,M=aim2_adc2_1,DTYP=asynMCA,INP=@asyn(AIM2/2 0),NCHAN=8192")
dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=mcaTest:,M=aim2_adc2_2,DTYP=asynMCA,INP=@asyn(AIM2/2 1),NCHAN=8192")

Note that it is convenient to set the multiplexor switches for the largest spectral size and largest number of inputs which you anticipate using. Smaller spectral sizes can be selected by setting NCHAN in the startup script (=NUSE and NMAX in MCA record) to a smaller value, without having to open the multiplexor and change the switch settings. Extra inputs are ignored simply by not assigning any records to those inputs. The only disadvantage of this approach is that it wastes AIM memory. However, the AIM has a total of more than 64,000 channels of acquisition memory which is more than enough for many applications.

ICB setup

There is a separate asyn server for ICB module (such as an ADC, amplifier, etc.) The configuration of an ICB module and creation of the asyn server is done by routine ICBConfig. ICBConfig is called as follows:

#icbConfig(portName, module, ethernetAddress, icbAddress, moduleType)

The following lines show how to set up ICB modules in an EPICS startup script. This assumes an AIM at address 0x59e, an ADC at ICB address 5, an amplifier at ICB address 3, a HVPS at ICB address 2, a TCA at ICB address 8, and a DSP at ICB address 7. The LIMIT parameter for the HVPS specifies the maximum voltage. The MCA parameter for the TCA specifies an associated MCA record for which programming the first three ROIs will program the TCA windows.

icbConfig("icbAdc1", 0x59e, 5, 0)
dbLoadRecords("$(MCA)/mcaApp/Db/icb_adc.db", "P=mcaTest:,ADC=adc1,PORT=icbAdc1")
icbConfig("icbAmp1", 0x59e, 3, 1)
dbLoadRecords("$(MCA)/mcaApp/Db/icb_amp.db", "P=mcaTest:,AMP=amp1,PORT=icbAmp1")
icbConfig("icbHvps1", 0x59e, 2, 2)
dbLoadRecords("$(MCA)/mcaApp/Db/icb_hvps.db", "P=mcaTest:,HVPS=hvps1,PORT=icbHvps1,LIMIT=1000")
icbConfig("icbTca1", 0x59e, 8, 3)
dbLoadRecords("$(MCA)/mcaApp/Db/icb_tca.db", "P=mcaTest:,TCA=tca1,MCA=aim_adc2,PORT=icbTca1")
#icbConfig("icbDsp1", 0x59e, 7, 4)
#dbLoadRecords("$(MCA)/mcaApp/Db/icbDsp.db", "P=mcaTest:,DSP=dsp1,PORT=icbDsp1")

DSA-2000 setup

The DSA-2000 contains 3 logical modules that are supported by EPICS.

  1. The MCA module is the same as an AIM with a single ADC port, and it is configured with the AIMConfig command described above.
  2. The DSP module is the same as the 9660 DSP ICB module, and is configured with the icbConfig command described above, with ICB address 0 and module type 4.
  3. The HVPS is unique to the DSA-2000 and is configured with the DSA2000Config command.

Example DSA-2000 startup script

The following startup script configures a DSA-2000 MCA, DSP, and HVPS.

AIMConfig("AIM1/1", 0x8058, 1, 2048, 1, 1, "eth1")
dbLoadRecords("$(MCA)/mcaApp/Db/mca.db", "P=mcaTest:,M=aim_adc1,DTYP=asynMCA,INP=@asyn(AIM1/1 0),NCHAN=2048")

icbConfig("icbDsp1", 0x8058, 0, 4)
dbLoadRecords("$(MCA)/mcaApp/Db/icbDsp.db", "P=mcaTest:,DSP=dsp1,PORT=icbDsp1")

DSA2000Config("DSA2000", 0x8058)
dbLoadRecords("$(MCA)/mcaApp/Db/DSA2000_HVPS.db", "P=mcaTest:,HVPS=hvps1:,PORT=DSA2000,LIMIT=5000")

Utility commands

There are several commands for the Canberra support that are defined in the EPICS iocsh shell.

Examples of these commands:

epics> mcaAIMShowModules
Module     Owner name      Owner ID       Status      Memory size Free address
NI008058    ioc13lab  00:01:AF:0C:11:F7  Reachable     262140      00000000
NI00059e    gselab1   00:B0:D0:D9:A3:03  Reachable     261116      00004000
NI0006e6    ioc13idc  00:01:AF:00:74:CD  Reachable     261116      00000000
NI0009ce    ioc13bmd  08:00:3E:2E:63:37  Reachable     261116      00000000
NI0003ed    ioc13idd  08:00:3E:2F:1F:F8  Reachable     261116      00000000

epics> icbShowModules
NI59E:2   3  CI9641 2K HVPS     8965924
NI59E:3   7  CI9615 AMP         11923928
   Preamp type:       RC
   Coarse gain:       2.500000
   Fine gain:         1.000000
   Super fine gain:   1.000000
   Shaping mode:      GAUSSIAN
   Pole zero:         1
   Baseline restore:  SYM
   Dead-time control: Norm
   Time constant:     4.000000
   Flags:             10
NI59E:5   6  CI9635 8K ADC      2945853
   Range:             2048
   Offset:            0
   Acquisition mode:  PHA
   Conversion gain:   2048
   Lower level disc:  1.000000
   Upper level disc:  100.000000
   Zero:              0.000000
   Flags:             40
NI59E:8   2  Unknown            3994275

# Release the second module in the above list (NI00059e) even if this command
# is not being issued from the owner IOC, gselab1. 
epics> mcaAIMFreeModule 1 1

vxWorks configuration

The driver on vxWorks uses the functions in muxLib or muxTkLib. On vxWorks 6.x this requires that the Board Support Package (BSP) be built with the INCLUDE_NET_POOL option.

Linux configuration

The libnet (and possibly libpcap?) libraries need root permission in order to send non-TCP/IP packets on the network. Thus, the EPICS IOC application must be run by someone with root privilege, or else the application must be installed with SUID root.

Windows configuration

The following must be installed:

It is necessary to specify the name of the Ethernet device in the AIMConfig command in the EPICS startup script. This is of the format:

\Device\NPF_{8ECC055D-047F-11D1-A537-0000F8753ED1}

The long number at the end is unique to each network card. The simplest way to get this name is to run with the example startup script in iocBoot/iocWindows/st.cmd with the string provided in the distribution. This will fail, but the driver will then print out a list of all the valid network device names on your system, along with their descriptions. Find the one that describes the network that your Canberra hardware is attached to, and replace the string in the AIMConfig commands below with that string.

Alternatively, you can get this number for your Window machine by using the "regedit" utility, and doing an "export" of the key:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards]

This should look like:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards]

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards\2]
"ServiceName"="{15B576D2-6DF4-4C9F-B53C-DBF76B53194E}"
"Description"="3Com 3C920 Integrated Fast Ethernet Controller (3C905C-TX Compatible)"

The number that is needed in the ServiceName field. Copy this number and paste into the AIMConfig command in your st.cmd startup command file.

Time-resolved measurements

For time-resolved spectroscopy it is very desirable to be able to rapidly change the region of the AIM memory into which an ADC acquires. The .SEQ field in the MCA record is designed for this purpose, and it is implemented in the AIM device support. By rapidly incrementing the value of the .SEQ field, for example with a database or Channel Access client, one can acquire a number of spectra in rapid succession, without incurring the delay of reading the spectra from the AIM and erasing the memory between acquisitions. The only delay is the few msec required to transmit a message changing the memory address of acquisition.

Performance Measurements


Tests were done with R6-10 of the mca module to measure the performance of the Canberra AIM modules in rapidly collecting spectra in a scan. The tests were done under the following conditions:

Performance Measurements: Time to collect 100 spectra

Model Host Compressed read Uncompressed read
556 Linux (libnet/libpcap) 10.2 15.8
556 vxWorks (etherLib) 10.0 14.4
556 vxWorks (LLC sockets, muxLib) 10.0 N.D.
556A Linux (libnet/libpcap) 35.0 10.8
556A vxWorks (etherLib) 30.0 10.1
556A vxWorks (LLC sockets, muxLib) N.D. 10.0
DSA2000 Linux (libnet/libpcap) 30.8 13.4
DSA2000 vxWorks (etherLib) N.D. 12.0
DSA2000 vxWorks (LLC sockets, muxLib) N.D. 12.0

The following IDL program measures how fast time-resolved data can be acquired. It was tested with record 13LAB:aim_adc1 configured with maxSequences=4 and with an input pulser at about 100 kHz.

;************************************************************
; This program tests the time-resolved spectroscopy capabilities of the
; new MCA record with the AIM.  The hardware configuration is an ADC with
; pulses coming in at about 100 kHz.

; Create EPICS MCA object
rec = '13LAB:aim_adc1'
casettimeout, .005  ; Fast channel access timeout
max_sequences = 4
mca = obj_new('epics_mca', rec)

; Turn of acquisition, erase all of the spectra in the AIM
mca->acquire_off
for i=0,max_sequences-1 do begin
   mca->set_sequence, i
   mca->erase
endfor

; Acquire data in sequence 0 for 1 second to determine input count rate
mca->set_sequence, 0
presets = mca->get_presets()
presets.real_time = 1.0
presets.live_time = 0.0
mca->set_presets, presets
mca->erase
mca->acquire_on
mca->acquire_wait
data = mca->get_data()
count_rate = total(data)
print, 'Input count rate = ', count_rate

; Clear presets, erase sequence 0
presets.live_time = 0.0
mca->set_presets, presets
mca->erase

; Now collect time-resolved spectra as fast as possible
mca->acquire_on
t0 = systime(1)
for i=0,max_sequences-1 do begin
   mca->set_sequence, i
endfor
t1 = systime(1)
mca->acquire_off

print, 'Elapsed time for time-resolved spectra= ', t1-t0
; Read out each spectrum, print number of counts
time = fltarr(max_sequences)
for i=0, max_sequences-1 do begin
    mca->set_sequence, i
    data = mca->get_data()
    time(i) = total(data)/count_rate
    print, '  Total counts in sequence ', i, ' = ', total(data)
    print, '    time in sequence = ', time(i) * 1000., ' msec.'
endfor
print, 'Real time = ', t1-t0
print, 'Live time = ', total(time)
print, 'Dead time = ', total(time)/(t1-t0)*100., ' percent'
end
;************************************************************

The following are the results of the test:

Input count rate =       125459.
Elapsed time for time-resolved spectra=       0.24195898
  Total counts in sequence        0 =       8187.00
    time in sequence =       65.2564 msec.
  Total counts in sequence        1 =       3787.00
    time in sequence =       30.1852 msec.
  Total counts in sequence        2 =       3803.00
    time in sequence =       30.3127 msec.
  Total counts in sequence        3 =       2862.00
    time in sequence =       22.8122 msec.
Real time =       0.24195898
Live time =      0.148566
Dead time =        61.401506 percent

The total elapsed time to collect 4 time-resolved spectra is 241 msec. The first spectrum collected for 65 msec (because it includes the overhead of the acquire on command), while the other spectra were collected for 20-30 msec. The dead time was about 100 msec for 4 spectra, or about 25 msec per spectrum. Note that if the .SEQ field were incremented by a process in the crate, rather than IDL channel access the performance might be somewhat better.

The conclusion is that the MCA record and AIM can acquire about 20 spectra per second, which is fast enough for many applications.


Suggestions and comments to: Mark Rivers : (rivers@cars.uchicago.edu)