This document describes the software interface to a generic Industry Pack (IPAC) driver module for EPICS, which was originaly written as part of a CANbus EPICS device driver for the Gemini and UKIRT telescopes. The original purpose of the generic IPAC driver was to ensure that the CANbus driver would not be restricted to use with a single carrier board but could be used with different carriers as required, including with more than one type of carrier board simultaneously. The use of the generic driver also ensures that additional IPAC modules and drivers for other interfaces can be added without affecting the functioning of the CANbus driver.
To provide a generic IPAC carrier board interface for each IPAC module driver, all control of or requests for information about the carrier board goes via the IPAC driver which in turn calls the IPAC Carrier driver written for the particular type of carrier board. This carrier driver should be simple to write, comprising three or four short subroutines and an interface structure. Carrier drivers are available for most of the common IP carrier boards.
At present the IPAC driver is limited (by the size of an internal array) to controlling a maximum of 21 carrier boards, but this limitation should be easy to dispense with completely without altering any of its interfaces.
The drvIpac subsystem is an EPICS <supporttop> application which can also be used to build IPAC module drivers. To install and use the the ipac support, obtain a copy of the tar file or (if you have remote CVS permissions at APS) export the software from the CVS repository where it resides as epics/modules/bus/ipac - CVS tags of the form `V2-1' mark the particular file versions required for each release. These instructions assume you already have EPICS R3.14.x installed and built (this software should work with EPICS R3.14.4 and later). Two steps are then required to install and build the software:
From version 2-1 this software supports little-endian as well as big-endian busses, and provides a means of isolating module drivers from some of the differences between interrrupt handling on different busses.
The endian problem only exists when accessing I/O registers and the IPAC ID PROM using byte addresses. In drvIpac all accesses are made using 16-bit read/write cycles, so there are no known issues with endianness. This is done by declaring the structure of a module's registers using short variables instead of char. With this done the only other requirement is to mask off the top 8 bits of every value read from the hardware.
Some busses such as ISAbus do not support interrupt vectors and require a different approach to connecting Interrupt Service Routines up to the relevent hardware interrupts, although the IndustryPack standard does require that any IP module that generates interrupts should provide a vector. On ISA bus carriers this vector can be read by the carrier driver to work out which module caused the interrupt. A module driver should not need to know about the particular bus type its carrier is on, thus Ipac now provides the routine ipmIntConnect() to allow it to pass such issues off to the carrier driver to handle. The interface to this is very similar to the standard vxWorks intConnect() routine.
The driver provides a C header file drvIpac.h for use by both module and carrier drivers.
#include "drvIpac.h"
This header file declares the necessary structures, enumerated types and functions provided by the driver. These are individually documented below.
This software can no longer be built for use without EPICS.
Used to register a carrier board and the appropriate carrier driver software for it with the IPAC Driver. Up to release 2.5 this call was used directly inside IOC startup scripts, but from release 2.6 onwards the carrier drivers provided with drvIpac all contain their own routines for use in startup scripts, which allows initialization from the EPICS ioc shell as well as from the vxWorks shell. See the documentation for the specific carrier board in section 4 below for the name of the new initialization routine.
int ipacAddCarrier(ipac_carrier_t *pcarrier, const char *cardParams);
This routine will usually be called from the EPICS start-up script. Some types of carrier may need additional initialisation before or after registering, but this method using the card parameter string should be sufficient for most carriers. Note that only the carrier initialise() routine is called at this stage. The order in which carriers are registered with this routine defines the carrier number which they will be allocated, starting from zero for the first board registered.
The code checks that the carrier descriptor table looks sensible, calls the initialise routine with the given card parameters, then saves the carrier private pointer and carrier table address in an internal array. The card number allows the same descriptor table to be used for all carriers of the same type.
It may be necessary to remove a carrier temporarily from a system in some circumstances without wanting to have to change the carrier number allocated to higher numbered carriers. To allow this, it is legal to call this routine with a NULL (zero) carrier table address, which switches in the null carrier table instead. When this facility is used any module driver which attempts to access a slot on this carrier will be given error status returns by the module interface routines.
As long as the carrier table is not full, ipacAddCarrier() will always increment its internal carrier number on every call, thus a carrier driver failure will not cause all subsequent carriers to silently move down by one. In the event of an error, the null carrier table is used for the current carrier number instead of the requested table.
Symbol/Value | Meaning |
---|---|
0 | OK |
S_IPAC_tooMany | Carrier Info Table full |
S_IPAC_badTable | Carrier Table invalid |
(others values) | from carrier initialisation routine. |
ipacAddCarrier(&vipc610_01, "0x6000,256"); ipacAddCarrier(NULL, ""); ipacAddCarrier(&vipc310, "0x6800");
Gets the carrier number of the most recently added carrier board.
int ipacLatestCarrier(void);
Returns the index into the carrier table of the most recently added carrier board, or USHRT_MAX if the most recent call to ipacAddCarrier could not be fulfilled because the carrier table was already full. The value returned can always be used as the carrier argument to any drvIpac routine without checking it first; if the carrier board was not properly initialized for any reason then these routines will return a failure status of some kind.
Symbol/Value | Meaning |
---|---|
0 thru 20 | Carrier number of latest board added |
USHRT_MAX | Carrier table was full |
Prints a report on stdout giving the status of all known IPAC carriers.
int ipacReport(int interest);
Prints information on each known carrier board and slot according to the specified interest level. Level 0 lists all the carriers defined, with the number of IPAC slots each one supports. Level 1 gives details on each slot on the carriers: the Manufacturer and Model ID bytes of the installed module if one is present, and the Carrier Driver's report for that slot (see ipmReport below). Level 2 adds the CPU address of each memory space for the slot.
Symbol/Value | Meaning |
---|---|
0 | OK. |
Initialise the IPAC driver.
int ipacInitialise(int after);
Symbol/Value | Meaning |
---|---|
0 | OK. |
The routines documented below are provided for use by the module drivers which use the services of the generic IPAC driver. In general it is expected that these routines will only be used at initialisation time. The module driver should be informed by other means which carrier and slot the particular IPAC module it is to control is installed in, although it should be possible to search each carrier and slot number in turn for the module using the Manufacturer and Model ID codes.
Returns Base CPU address of selected IP address space
void *ipmBaseAddr(int carrier, int slot, ipac_addr_t space);
Together these two parameters uniquely identify a specific IPAC module in the system, and these are used in this way by all of the following routines.
IP Address Space space ID Prom Space ipac_addrID Register Space ipac_addrIO 32-bit Register Space ipac_addrIO32 Memory Space ipac_addrMem
Checks its input parameters, then calls the carrier driver. This will return a pointer to the location of the address space indicated by the space parameter.
All IP modules must provide an ID prom to indicate the module type (space = ipac_addrID). Most modules need register I/O locations, which are in the I/O space (space = ipac_addrIO). Some types of module also provide memory (space = ipac_addrMem), but if this is not required the carrier may allow it to be disabled, in which case the carrier driver will return a NULL for this address space. Some carriers also provide a 32-bit wide I/O space for accessing 32-bit registers on Dual-slot IP modules (space = ipac_addrIO32); carriers which do not support this will return NULL for this space.
Check on the presence of an IPAC module at the given carrier and slot number.
int ipmCheck(int carrier, int slot);
Does a quick check to make sure the carrier and slot numbers are legal, probes the IDprom space to ensure an IPAC is installed, and checks that the IDprom starts with an "IPAC", "IPAH" or "VITA4" identifier.
Symbol/Value | Meaning |
---|---|
0 | OK |
S_IPAC_badAddress | Bad carrier or slot number |
S_IPAC_badDriver | Carrier driver returned NULL ID address |
S_IPAC_noModule | No IP module installed |
S_IPAC_noIpacId | IP Module identifier not found |
Validates a particular IPAC module type at the given carrier & slot number.
int ipmValidate(int carrier, int slot, int manufacturerId, int modelId);
Uses ipmCheck to ensure the carrier and slot numbers are legal, probe the IDprom and check that the IDprom looks like an IPAC module. Then calculates and verifies the CRC for the ID Prom, and compares the manufacturer and model ID values in the Prom to the ones given.
The manufacturer and model identification numbers allow a Module Driver to ensure that the correct hardware has been installed in the particular slot which the driver has been told to control. If a driver supports more than one type of module, it should check each module type individually by calling ipmValidate with each manufacturer/model pair it can control until it finds a match.
Releases of drvIpac before 2-9 did not recognize Format-2 ID PROMS as defined in the VITA spec. From release 2-9 on this software should be compatible with modules having either Format-1 or Format-2 ID PROMS. In the newer format the PROM is specified to be 16 bits wide rather than 8, and various fields including the Manufacturer and Model numbers have been made wider. The calculated CRC is also wider, but some IP Module manufacturers are setting the CRC field to all zeros rather than calculating the correct CRC value for it. As a result, the ipmValidate routine will not check the CRC of a Format-2 ID Prom if its CRC field is zero.
Symbol/Value | Meaning |
---|---|
0 | OK |
S_IPAC_badCRC | CRC Check failed |
S_IPAC_badModule | Manufacturer or model IDs wrong |
S_IPAC_badAddress | Bad carrier or slot number |
S_IPAC_badDriver | Carrier driver returned NULL ID address |
S_IPAC_noModule | No IP module installed |
S_IPAC_noIpacId | IP Module identifier not found |
Manipulate the carrier board interrupt controller.
int ipmIrqCmd(int carrier, int slot, int irqNumber, ipac_irqCmd_t cmd);
Checks input parameters, then passes the interrupt command request to the equivalent Carrier Driver routine. The driver is only required to support the command ipac_irqEnable; for other commands it may return the status code S_IPAC_notImplemented and do nothing. Commands available are as follows:
Command Description cmd Selects Interrupt Priority 0 (disabled) ipac_irqLevel0 Selects Interrupt Priority 1 ipac_irqLevel1 Selects Interrupt Priority 2 ipac_irqLevel2 Selects Interrupt Priority 3 ipac_irqLevel3 Selects Interrupt Priority 4 ipac_irqLevel4 Selects Interrupt Priority 5 ipac_irqLevel5 Selects Interrupt Priority 6 ipac_irqLevel6 Selects Interrupt Priority 7 (non-maskable) ipac_irqLevel7 Returns current Interrupt Priority, 0 to 7 ipac_irqGetLevel Enable interrupts from module ipac_irqEnable Disable interrupts from module ipac_irqDisable Returns current interrupt signal state ipac_irqPoll Sets LED indicator to Empty/Unused ipac_statUnused Sets LED indicator to Active ipac_statActive Resets this IP slot only ipac_slotReset
The Interrupt Priority (often also known as interrupt level) commands are defined to be numerically the same as the level number they select. Level 0 effectively disables the interrupt. Level 7 is not recommended within vxWorks as it is non-maskable, and may only be used for an ISR that does not call any vxWorks routines. The ipac_irqGetLevel command returns the level currently set. Carrier boards which have fixed interrupt levels will not support setting the interrupt level but the driver may still able to return the level number.
The ipac_irqEnable command must be supported by all Carrier Drivers, and must be called by any Module Driver that uses interrupts. The Carrier Driver routine is responsible for calling the vxWorks sysIntEnable() routine if this is required to allow IPAC interrupts to be seen by the CPU. The corresponding ipac_irqDisable command is not necessarily supported by all carriers however - the module driver must disable interrupts using the control registers on the IP module itself if this is necessary. A Carrier Driver cannot implement the ipac_irqDisable command with a call to the vxWorks sysIntDisable() routine because this would stop any other devices which still need this interrupt level from working.
The ipac_slotReset command should only be implemented on carrier boards that can reset IP slots individually without affecting any IP modules in neighboring slots. This command must not return until the reset process has completed, so any driver using this may immediately proceed to reinitialize the IP module as required.
Symbol/Value | Meaning |
---|---|
0 | OK |
S_IPAC_badAddress | No such carrier or slot |
S_IPAC_notImplemented | Driver does not support that command |
Other values may also be returned depending on the Driver and command used.
Connects a module driver Interrupt Service Routine to a particular interrupt vector number.
int ipmIntConnect (int carrier, int slot, int vecNum, void (*routine)(int parameter), int parameter);
Checks input parameters, then passes the request to the carrier driver routine. If no carrier routine is provided it uses the standard vxWorks intConnect() routine instead. This is not quite a direct replacement for the vxWorks intConnect() call; as well as providing the carrier and slot numbers the module driver does not use the INUM_TO_IVEC(vecNum) macro but just passes the vector number to this routine.
VxWorks' interrupt vectoring mechanism varies between bus types, and ipmIntConnect() allows a module driver to connect its routine to an interrupt vector from a particular IPAC module without knowing the requirements of the particular bus type. Some carrier drivers will need to maintain a private interrupt dispatch table if the bus type (i.e. ISA) does not support interrupt vectoring.
Symbol/Value | Meaning |
---|---|
0 | OK |
S_IPAC_badAddress | No such carrier, slot or vector |
Other values may also be returned depending on the Driver and vector used.
char *ipmReport(int carrier, int slot);
Generates a report string describing the given IPAC slot. If a module is installed, it includes the manufacturer and model ID numbers. If the report function is supported by the carrier driver this report string is appended. This function is uses a static character array to hold the report string, thus the value will be corrupted if two tasks use this routine simultaneously. The string returned by the carrier driver's report routine will be clipped if longer than IPAC_REPORT_LEN characters.
C0 S0 : 0x23ae80/0x8d49 - M0 L3,3 C0 S1 : 0xB1/0x01 - M0 L4,5
This string is made up of three parts. The first two elements before the colon give the carrier and slot number of the slot. If a module is installed, the manufacturer and model IDs follow as two hex numbers (2 digits each for Format-1 ID PROMS, 6+4 digits for Format-2 ID PROMS). Finally if the Carrier Driver contains a report function it is called and the string it returns is appended after a hyphen.
This board is probably the simplest possible VME-based IPAC Carrier available. A 3U VME card, it provides two IP slots (although it cannot support 32-bit accesses to dual-slot IP modules), and allows no control over the slot interrupt controllers from software. The base addresses for the card are set using a series of jumpers on the board which select both the VME short I/O base address (used for the IPAC ID Prom and Register spaces) and the VME Standard base address (used for the IPAC Memory space if required). Additional jumpers allow the size of the IPAC Memory to be selected. The interrupts are at priority levels fixed by the hardware.
The IPAC Carrier Driver for this board is found in the file drvVipc310.c which implements the command ipacAddVIPC310 to add a VIPC310 board to the system, and provides the registrar routine vipc310Registrar to add the above command to the iocsh and link the driver into a final IOC executable, for which it must be listed in the IOC's .dbd file thus:
registrar(vipc310Registrar)
int ipacAddVIPC310(const char *cardParams);
The parameter string should comprise a hexadecimal number (an 0x or 0X at the start is optional) optionally followed by a comma and a decimal integer. The first number is the I/O Base Address of the card in the VME A16 address space. The factory default setting for the card gives an I/O Base Address of 0x6000, and this value will be used if the string is empty or NULL. If supplied the second number in the parameter string gives the size in Kbytes of the memory space allocated to each IP module.
The Memory Base Address of the VIPC310 card is set using the same jumpers as the I/O base address and is always 256 times the I/O base address, but in the VME A24 address space. The factory default for the memory base address is thus 0x600000.
If the memory size parameter is omitted or set to zero then neither IP module provides any memory space. Legal memory size values are 0, 64, 128, 256, 512, 1024 or 2048. The memory size interacts with the memory base address such that it is possible to set the existence of memory in either slot independently by suitable adjustment of the base address.
The board uses fixed interrupt levels, and provides no software control over the interrupt generator. The only commands thus supported are a request of the interrupt level associated with a particular slot and interrupt number, or to enable interrupts by making sure the CPU's VMEbus interrupter is listening on the necessary level.
cmd Value Returned ipac_irqGetLevel slot interrupt level (1, 2, 4 or 5) ipac_irqEnable 0 = OK (other commands) S_IPAC_notImplemented
This board is a four-slot, 6U carrier, but otherwise very similar to the VIPC310. A seperate driver is also included for the VIPC610-01 option which changes the Interrupt Priority levels for the IP slots so they are equivalent to a pair of VIPC310 carrier boards, different to the interrupt levels for a standard VIPC610 board.
The IPAC Carrier Drivers for both board versions are found in the file drvVipc610.c which implements the commands ipacAddVIPC610 and ipacAddVIPC610_01 for adding a VIPC610 board of the relevent type to the system, and provides the registrar routine vipc610Registrar to add the above commands to the iocsh and link the driver into a final IOC executable, for which it must be listed in the IOC's .dbd file thus:
registrar(vipc610Registrar)
Warning: As delivered by SBS the VIPC610 has IRQ0 of slot D connected to the VMEbus level 7 interrupt, and IRQ1 of slot D does not cause a VMEbus interrupt at all. Under vxWorks on the MC680x0 family a level 7 interrupt level may only be connected to a service routine that does not make any calls to vxWorks routines. Thus with a VIPC610 board slot D should not be used for modules that generate interrupts. The VIPC610-01 board option is free of this problem.
int ipacAddVIPC610(const char *cardParams); int ipacAddVIPC610_01(const char *cardParams);
The parameter string should comprise a hexadecimal number (an 0x or 0X at the start is optional) optionally followed by a comma and a decimal integer. The first number is the I/O Base Address of the card in the VME A16 address space. The factory default setting for the card gives an I/O Base Address of 0x6000, and this value will be used if the string is empty or NULL. If supplied the second number in the parameter string gives the size of the memory space in Kbytes allocated to each IP module.
The Memory Base Address of the VIPC610 card is set using the same jumpers as the I/O Base Address and is always 256 times the I/O Base Address, but in the VME A24 address space. The factory default for the Memory Base address is thus 0x600000.
If the memory size parameter is omitted or set to zero then none of the IP modules on the carrier provide any memory space. Legal memory size values are 0, 64?, 128, 256, 512, 1024 or 2048. The memory size interacts with the Memory Base Address setting such that it is possible to exclude memory from the lower slots while still providing access to memory in the later slots by suitable adjustment of the base address.
The board uses fixed interrupt levels, and provides no software control over the interrupt generator. The only commands thus supported are a request of the interrupt level associated with a particular slot and interrupt number, or to enable interrupts by making sure the CPU's VMEbus interrupter is listening on the necessary level. Note that the VIPC610-01 uses different interrupt levels to the straight VIPC610, you must use the correct driver!
cmd Value Returned ipac_irqGetLevel slot interrupt level ipac_irqEnable 0 = OK (other commands) S_IPAC_notImplemented
This board is a four-slot, 6U carrier, but otherwise very similar to the VIPC310. A seperate driver is also included for the VIPC616-01 option which changes the Interrupt Priority levels for the IP slots so they are equivalent to a pair of VIPC310 carrier boards, different to the interrupt levels for a standard VIPC616 board.
The only differences between this carrier and the VIPC610 relate to the VME addressing capabilities, the VIPC616 supports access to memory IP modules in the VME A32 address space. It is possible to use the VIP610 driver to control a VIPC616 board, but the use of this specific driver is recommended. This driver should also be used for the VIPC618 carrier which is schematically identical to the VIPC616 but uses different I/O connectors for the IP module signals.
The IPAC Carrier Drivers for both board versions are found in the file drvVipc616.c which implements the commands ipacAddVIPC616 and ipacAddVIPC616_01 to add a VIPC616 board of the relevent type to the system, and provides the registrar routine vipc616Registrar to add the above commands to the iocsh and link the driver into a final IOC executable, for which it must be listed in the IOC's .dbd file thus:
registrar(vipc616Registrar)
Warning: As delivered by SBS the VIPC616 has IRQ0 of slot D connected to the VMEbus level 7 interrupt, and IRQ1 of slot D does not cause a VMEbus interrupt at all. Under vxWorks on the MC680x0 family a level 7 interrupt level may only be connected to a service routine that does not make any calls to vxWorks routines. Thus with a VIPC616 board slot D should not be used for modules that generate interrupts. The VIPC616-01 board option is free of this problem.
int ipacAddVIPC616(const char *cardParams); int ipacAddVIPC616_01(const char *cardParams);
The parameter string should comprise a hexadecimal number (an 0x or 0X at the start is not required) optionally followed by a comma and another hexadecimal number, and then possibly another comma and a decimal integer. The first number is the I/O Base Address of the card in the VME A16 address space. The factory default setting for the card gives an I/O Base Address of 0x6000, and this value will be used if the string is empty or NULL.
The meaning of the second number depends upon whether the third (decimal) integer is present in the string or not. If there is no third number then the second number gives the Memory Base Address of the card in the VME A32 space. In this case each module is allocated a fixed 8 Mbytes of memory space by the carrier.
If all three numbers are given then the second number is the Memory Base Address of the card in VME A24 address space, and the third number gives the size of the memory space in Kbytes allocated to each IP module. This is the VIPC610 compatibility mode, and the Memory Base Address and Memory Size parameters are used and interact in exactly the same way as with the VIPC610.
The board uses fixed interrupt levels, and provides no software control over the interrupt generator. The only commands thus supported are a request of the interrupt level associated with a particular slot and interrupt number, or to enable interrupts by making sure the CPU's VMEbus interrupter is listening on the necessary level. Note that the VIPC616-01 uses different interrupt levels to the straight VIPC616, you must use the correct driver!
cmd Value Returned ipac_irqGetLevel slot interrupt level ipac_irqEnable 0 = OK (other commands) S_IPAC_notImplemented
This board is a four-slot, 6U carrier which is also sold by SBS as the VIPC626 and was also available from XYCOM VME. The configuration of the board is much simpler than the VIPC boards it replaces as there are only 6 rotary hexadecimal switches which completely configure the board. There are also a pair of registers provided for each slot, which allow the interrupt levels to be read and configured in software and permit individual slots to be reset.
It is possible to use the VIP610 or VIPC616 drivers to control an appropriately configured TVME-200 board, but the use of this specific driver is strongly recommended if possible.
The IPAC Carrier Driver is found in the file drvTvme200.c which implements the command ipacAddTVME200 that registers a TVME-200 board with drvIpac. It also exports the registrar routine tvme200Registrar to add the above command to the iocsh and link the driver into a final IOC executable, for which it must be listed in the IOC's .dbd file thus:
registrar(tvme200Registrar)
int ipacAddTVME200(const char *cardParams);
The parameter string must contain exactly 6 hexadecimal digits (without any leading 0x or 0X) which are the settings of the six rotary switches on the board, in order from S1 through S6. The board's silk-screen printing shows the possible values and meaning for each switch position. The carrier initialization routine checks the switch settings for legality and will return an error if it finds an unsupported (undocumented) setting, as there are some combinations of switch settings that are reserved or not permitted.
The board provides a configuration switch to select from 5 different fixed VME interrupt level configurations, but it also provides software control over the interrupt generator which allows these levels to be changed under program control as needed. The commands supported therefore include the ability to get or set the interrupt level, and also to poll whether the interrupt line is currenly active or not. A control register bit also permits each slot to be reset individually.
cmd Value Returned ipac_irqLevelN 0 = OK ipac_irqGetLevel slot interrupt level ipac_irqEnable 0 = OK ipac_irqPoll >0 if the interrupt line is active, else 0 ipac_slotReset 0 = OK (other commands) S_IPAC_notImplemented
The SBS ATC40 is an IP carrier board for the ISAbus, which is a little-endian architecture unlike the VMEbus. Providing support for this board has required some changes to the drvIpac software which are described in the Bus Issues section above. Unless similar precautions are taken when writing module drivers these will not be compatible with little-endian systems. This carrier driver was written by Peregrine McGehee and Jeff Hill, who should be approached directly for support. Note that the carrier driver will probably only run on an Intel CPU, and has not been tested with recent drvIpac releases.
The registrar entry for this driver should be:
registrar(atc40Registrar)
The configuration command is shown below. See the source code for the format of the parameter string.
int ipacAddATC40(const char *cardParams);
The Motorola MVME162 and MVME172 CPU boards provide either two or four IP slots in addition to the MC68040 CPU, memory and I/O. Slot pairs can be used with 32-bit dual-slot IP modules, and the IPIC chip which controls the interface supports all of the IPAC Driver interrupter commands. This carrier driver can be used with either board; non-existant slots will always appear to be empty.
When this CPU board is used the IPAC carrier driver drvIpMv162.c can control the IP slots on the board. It implements the command ipacAddMVME162 to add the MVME162 or MVME172 carrier to the system, and provides the registrar routine mv162ipRegistrar to add the above command to the iocsh and link the driver into a final IOC executable, for which it must be listed in the IOC's .dbd file thus:
registrar(mv162ipRegistrar)
The carrier initialisation routine has the following additional return-code meanings (see also ipacInitialise() above):
Symbol/Value Meaning S_IPAC_tooMany IpMv162 carrier already registered S_IPAC_badDriver IPIC chip not found S_IPAC_badAddress Parameter string error, or address not reachable
int ipacAddMVME162(const char *cardParams)
The parameter string is used to initialise the IPIC registers which allow detailed control of several settings for each slot. The string consists of a series of single characters to determine the slot and setting to be controlled and one or more numeric parameters for each setting. The string is parsed sequentially from left to right, and the characters and their parameters have the following meanings:
Other characters will be ignored without affecting the parsing of the remainder of the parameter string.
ipacAddMVME162("A:m=0x90000000,1024 l=5,3 w=8 r=2 B:l=2")
The above example initialises slots 0 and 1 only - slots 2 and 3 are not used or their modules require register and ID Prom access only in this particular application.
Slot 0 is set up for a slow (recovery time 2µs) memory board with 1 Mbyte of 8-bit wide RAM, addressed at 0x90000000 and with interrupt priorities set for levels 5 and 3.
Slot 1 has no memory space, and just a single interrupt at priority level 2.
The IPIC chip allows a lot of control over the IP interrupters, thus all commands perform the requested action. The ipmIrqCmd return values for the commands are:
cmd Value Returned ipac_irqGetLevel current slot interrupt level ipac_irqPoll >0 if the interrupt line is active, else 0 Other ipac_irq commands 0 = OK Other commands S_IPAC_notImplemented
These boards are four-slot, 6U VME carriers, two of which were once sold by Xycom as the XVME-9660/9670. On the 9660 board the IP module signal wiring is brought out through front panel connectors, whereas the 9670 boards make signal wiring available through the VME64x P2 and P0 connectors. The AVME-9668 is like the 9660 but can operate IP modules at 8MHz or 32MHz, whereas the other boards only support 8MHz operation. A single bank of jumpers provides the only hardware configuration, setting the base address of the board in VME A16 space; module memory locations in VME A24 space and the module clock speeds for the 9668 are configured in software using the board registers.
The IPAC Carrier driver is found in the file drvXy9660.c and implements the commands ipacAddAvme96XX and ipacAddXy9660 that register a single carrier board (of any of the supported models) with drvIpac. The driver also exports a registrar routine xy9660Registrar that adds the above command to the iocsh and will link the driver into a final IOC executable, for which it must be listed in the IOC's .dbd file thus:
registrar(xy9660Registrar)
int ipacAddXy9660(const char *cardParams); int ipacAddAvme96XX(const char *cardParams);
The parameter string starts with a hex number (a leading 0x is optional) which sets the I/O base address of the card in the VME A16 address space (the factory default is 0x0000). Next must come a comma, followed by the VME interrupt level (0 through 7, although 0 means all interrupts are disabled) to be used for this carrier (all module interrupts share the same interrupt level from this board).
A letter R may appear at this point, which causes a carrier soft reset to be generated before any further configuration occurs.
Finally if any module drivers need to access the memory space on their module, the relevent slots must have their memory size and base address configured, which is done like this for each slot:
ipacAddXy9660("0x6000,4 R")
This indicates that the carrier board has its I/O base set to A16:6000 and that it generates interrupts on VME IRQ4. None of the slots are configured for memory, and modules are reset at initialization.
ipacAddAvme96XX("C000,3 A=2,800000 C=1,A00000")
The carrier is at A16:C000 and generates level 3 interrupts. Slot A is configured for 2MB of memory space at A24:800000 and Slot C for 1MB of memory space at A24:A00000.
Writing a new Carrier Driver is quite simple, and requires just three subroutines to be produced (the report function is optional). The driver will need some of the definitions in the drvIpac.h file.
#include "drvIpac.h"
All routines must be re-entrant, and no static variables should be used unless (like the mv162) only one carrier board of this type is possible in a particular system. In this case re-entrancy is still necessary, but static variables may be used to hold information about the carrier provided these are protected from simultaneous updates by different tasks.
The sole interface between the Carrier Driver and the IPAC driver is through the ipac_carrier_t typedef structure given in the header file. Note that this has changed slightly since version 2.0 with the addition of the carrier parameter to the initialise() routine, and the new intConnect() routine. This is defined as follows:
typedef struct { char *carrierType; /* String describing carrier board type */ int numberSlots; /* Number of IPAC devices this carrier can hold */ int (*initialise)(char *cardParams, void **cPrivate, ushort_t carrier); /* Initialise carrier and return *cPrivate */ char *(*report)(void *cPrivate, ushort_t slot); /* Return string giving status of this slot */ void *(*baseAddr)(void *cPrivate, ushort_t slot, ipac_addr_t space); /* Return base addresses for this slot */ int (*irqCmd)(void *cPrivate, ushort_t slot, ushort_t irqNumber, ipac_irqCmd_t cmd); /* Interrupt control */ int (*intConnect)(void *cPrivate, ushort_t slot, ushort_t vecNum, void (*routine)(int parameter), int parameter); /* Connect routine to interrupt vector */ } ipac_carrier_t;
The first two structure members provide fixed information about the carrier to the IPAC driver. carrierType is a string which is printed by the report function to identify the type of carrier board, and numberSlots indicates how many IPAC slots this particular type of carrier provides.
The remaining structure members are function pointers to the routines which control the carrier board. The cPrivate parameter passed to these functions points to a structure which must be defined and allocated by the carrier driver and can be used to hold information about a particular carrier board (for example the board base address). The IPAC driver stores the cPrivate pointer which is returned by the initialise() routine, and passes the same pointer back to the other routines when referring to that particular carrier board at a later stage.
The intConnect() function pointer may be set to NULL if the standard vxWorks intConnect() provides all the funtionality needed to install an interrupt vector - this will probably only be needed for ISA bus carriers.
The other parameters to the routines are identical to those described in detail in the corresponding IPAC Driver routines above. The IPAC Driver performs parameter checking on the slot and irqNumber parameters before calling the Carrier Driver routine, so these values can be used with confidence.
The simplest way to write a carrier driver is to copy the VIPC310 or MVME162 driver and modify one of these for the new board type. The MVME162 driver interfaces to the IPIC chip and can be used as a basis for carrier drivers which provide extensive control over the IPAC slots. The VIPC310 and VIPC610 boards are totally dumb, and thus provide the simplest possible example of a carrier driver.