Since LabVIEW does not support the concept of callbacks, LabVIEW cannot respond to interrupts the same way that a Windows driver would. What VISA provides is a mechanism for detecting interrupts. VISA informs LabVIEW that an interrupt occurred by sending an event message. When a VISA event has been received, LabVIEW can proceed to handle the interrupt.
VISA generates an event whenever an interrupt occurs, but there is no way to immediately inform LabVIEW that an event happened. LabVIEW applications must use the VISA Wait On Event VI to determine when an interrupt occurs. The interrupt sensing and processing sequences are described in the following sections.
The experienced Windows programmers needs to realize that creating a LabVIEW driver requires a paradigm change. The concept of callbacks or ‘event driven programming’ needs to be replaced with an approach based on ‘data flow programming’.
PCI Interrupt Sequence Summary
In a simplified view, PCI interrupts occur in the following sequence:
1. The operating system loads the device’s interrupt configuration information at boot time. For Windows and LabVIEW Real-Time, the interrupt configuration is stored in the .INF file.
2. The instrument driver configures the device to generate interrupts under device-specific conditions.
3. The device driver handles each interrupt as it occurs with the following sequence of events:
A. The device generates an interrupt that matches the interrupt detection algorithm defined in the INF file.
B. The operating system routes the interrupt to the VISA kernel driver, which polls each device until it locates the device that is generating the interrupt.
C. The VISA kernel driver then disables the interrupt by masking it on the device, using the interrupt removal sequence defined in the INF file.
D. The VISA kernel driver generates a VISA
VI_Event_PXI_INTR event causes an interrupt service handler provided by the instrument driver to be executed.
F. The interrupt service handler processes the interrupt, clears the interrupt condition, and re- enables interrupts for that device.
4. When the application is no longer interested in receiving interrupts, the instrument driver disables interrupts for the device.
To configure interrupts the user application should call a function provided by the driver API that programs the CSR registers so that the device will generate interrupts under certain conditions if hardware interrupts are enabled. This function should not enable hardware interrupts on the device.
The driver must call the VISA Enable Event to enable VISA events for the session before enabling hardware interrupts on the device. The event you need to enable is the PXI Interrupt Event. If the device is allowed to generate interrupts before VISA events have been enabled, there will be no mechanism to remove the first interrupt, resulting in a system lockup condition. To enable hardware interrupts, the user application should call a VI provided by the driver API that programs the CSR registers for this purpose. It is good practice to include the VISA Enable Event call in this function.
Enabling interrupts is usually performed as part of the device initialization sequence, but may be used (with the Interrupt disable function) to control interrupt generation at any point in the user application.
Interrupt Detection and Removal
Interrupt detection and removal consists of a sequence of register read/write operations performed by the VISA kernel driver that detects whether the device is generating an interrupt by reading values from the CSR registers, and disables hardware interrupts on the device by writing specific values to the CSR registers.
The interrupt detection and removal sequences are defined in an .INF file associated with each device type. For Windows and LabVIEW Real-Time, you can use the VISA Driver Development Wizard to configure these algorithms.
Configuring the VISA Event Queue
VISA places events in the event queue every time an interrupt is detected. The driver must enable PXI Interrupt events by calling the VISA Enable Event VI with an event type parameter of VI_EVENT_PXI_INTR.
The size of the VISA event queue can be modified depending on the application. If the interrupt handling routine takes a considerable amount of time processing the interrupt, it may be desirable to increase the event queue size to ensure that no interrupt is lost. The driver can change the event queue size using the VISA Max Queue Length property.
The event queue defaults to a value of 50. Visa does not let you dynamically configure queues lengths. That is, you can only modify the queue length for a given session before the first invocation of the VISA Enable Event VI.
In many cases, the interrupt processing requirements are application-specific, so the driver API should include functions commonly required by user applications for this purpose.
The following diagram provides an overview of the interrupt processing procedure:
Figure 3. Interrupt Processing Procedure
The methods used to process interrupts vary between devices and even between applications using the same device. Suggested steps for processing an interrupt are described below. These steps take place after the VISA Wait On Event VI indicates that an interrupt occurred.
1. Determine the interrupt source. Devices often have multiple sources that can generate an interrupt. It is the driver’s responsibility to query the device to determine the source of each interrupt.
2. Service the interrupt. There are 2 main approaches a driver developer can take:
a. Implement a function that services interrupts in a predefined way. This approach may be useful when interrupt processing is not application-specific. You could wrap this type of interrupt processing function together with the functionality of the previous step to provide the user with a one-step interrupt processing implementation.
b. Allow the user application to provide code that services the interrupt in an application-specific way. This approach is useful when interrupt servicing is application specific, and is similar to a Windows driver in the sense that the driver allows for user-defined functions to be called whenever an interrupt occurs. The driver may need to provide additional helper functions so that the user application can directly interact with the device.
3. Re-enable interrupts on the device. When a device generates an interrupt, VISA automatically disables hardware interrupts on that device. It is the driver’s responsibility to re-enable the device’s interrupts after the interrupt has been serviced. If you are using the approach described in 2b, one of your helper functions should be a function that re-enables hardware interrupts on the device. The user application is responsible for calling this function after the application-specific code completes execution.
The interrupt processing mechanism can be exposed as a set of VIs in the driver API. The user application calls the interrupt processing mechanism periodically. When called, it senses interrupt events by calling the VISA Wait On Event VI . If no interrupts have occurred, the VISA Wait On Event VI and the interrupt processing mechanism should be programmed to return immediately (timeout = 0). This approach allows a real-time application to call the interrupt processing mechanism only when a VISA interrupt event has occurred. Note, however, that the worst-case interrupt latency using this approach depends on how often the application calls the interrupt processing mechanism.
The driver implementation of the interrupt processing mechanism should not include a loop around VISA Wait On Event VI, that waits for an interrupt to occur. By design, most LabVIEW applications include a loop and in some cases, this is a time-critical loop. The application developer must include the driver functions to handle interrupts inside their application loop. If your interrupt processing implementation already contains a loop on the VISA Wait On Event VI, you may starve the time critical loop and affect determinism. Your implementation should call the VISA Wait On Event VI once and return immediately whether an interrupt occurred or not. The application’s execution flow is the responsibility of the application developer, not the driver developer.
In summary, a good implementation practice is to create several driver functions for processing interrupts that include:
- An interrupt event sensing function that calls the VISA Wait On Event VI once, and interrogates the interrupt source
- Helper functions for servicing interrupts
- A helper function for re-enabling hardware interrupts on the device.
Interrupts must be disabled on the device before the device session is closed.
After interrupts are disabled (but before the session is closed), the driver must also call the VISA Disable Event VI to disable VISA interrupt events for the session.
See the VMIC 5565 driver (previously referenced) for an example interrupt handler implementationi
Tip: As mentioned earlier, the LabVIEW Real-Time mechanism for handling interrupts consists on calling the WaitOnEvent VISA function to verify if an interrupt occurred. Because of this, the function/VI that includes the WaitOnEvent call will be called periodically in a loop. Make sure that your interrupt service handler is optimized to be called repeatedly. Avoid practices that would slow it down or affect determinism.
There are numerous interrupt handler implementation errors that can cause unintended system behaviors. Here are some of the more common causes and effects.
|Spurious interrupt service handler calls||Incorrect interrupt detection sequence– False positive.||Detection algorithm in INF file senses that the device is generating interrupts when it is not. Another device is generating the interrupts.|
|Erratic or no interrupt service handler calls (unexpected Wait on Event timeouts, etc.)||Incorrect device interrupt configuration.||The device does not generate interrupts at the right time, or at all.|
|Erratic or no interrupt service handler calls (unexpected Wait on Event timeouts, etc.)||Interrupt service handler is not re-enabling interrupts on the device.||After the first interrupt, the device will not generate further interrupts.|
|System lockup||Incorrect interrupt detection sequence– False negative.||Detection algorithm does not sense interrupts generated by the device, so the removal sequence is not executed. This causes in an unsquelched interrupt condition.|
|System lockup||Incorrect interrupt removal sequence.||Removal algorithm does not successfully disable the interrupt on the device, resulting in an unsquelched interrupt condition.|
|System lockup||Driver enables hardware interrupts on the device before calling viEnableEvent.||Interrupt detection and removal sequence for the device are not active, resulting in an unsquelched interrupt condition.|
|System lockup||Driver calls viDisableEvent before disabling hardware interrupts on the device.||Interrupt detection and removal algorithms for the device are not active, resulting in an unsquelched interrupt condition.|
An unsquelched interrupt condition will cause the system to lock up because the VISA kernel driver never returns from the interrupt handler.
Bus Master DMA Support
DMA operations are useful for transferring large amounts of data between the device and the host without consuming CPU time. There are two basic DMA operations: DMA Read (transfers data from the device to the host) and DMA Write (transfers data from the host memory to the device).
When using VISA, DMA transfers occur between the devices memory and a buffer of locked memory on the host computer. VISA functions can be used to allocate contiguous, locked memory buffers on the host controller for use in bus master DMA operations.
Preparing for DMA operations
DMA operations require contiguous memory since the DMA controller increments the memory pointers sequentially. The memory also needs to be locked (non pageable) so that the DMA controller can ensure that the buffer will be available throughout the whole operation. Because of this, DMA operations must be performed using buffers allocated with VISA and not with memory buffers allocated in LabVIEW.
The following steps are required for setting up a DMA operation:
1. Open a session to a VISA Memory Access (MEMACC) Resource by calling the VISA Open VI with the VISA resource name PXI0::MEMACC . Note that this VISA session is separate from the device session and will need to be passed to all the subsequent functions that handle the VISA memory allocation. This opens a session to memory in the host controller.
2. Call the VISA Memory Allocation VI, to allocate and reserve the memory buffer. This function will return an offset value that can be used to program the DMA controller on the device.
3. Initialize your DMA controller. This step depends on the type of controller. Check the DMA controller’s documentation for specific initialization information.
Warning: Make sure that the driver treats the DMA transfer size and the allocated buffer size consistently. Depending on the device, the DMA transaction units maybe specified as byte, short or long words. The VISA Memory Allocation VI, however always specifies the buffer size in bytes. When setting up a DMA data transfer, make sure that the driver allocates enough memory for the whole transaction. For example, if the DMA controller transfers data as long words (4 bytes) and the transfer length is set to 1000, the DMA controller will transfer a total of 4000 bytes (1000 long words). The driver must specify 4000 (and not 1000) as the allocation size using the VISA Memory Allocation VI. The DMA controller will continue to increment its memory pointers until the operation is completed, so allocating less memory than required will cause other memory to be overwritten, which in turn may result in corrupted data or a system crash.
Performing DMA Read and Write operations
After performing a DMA Read operation, the driver must copy the data placed in the locked buffer into a user buffer using VISA Move In VI. Similarly, before performing a DMA Write operation, the driver must copy the user data into the locked memory using the function VISA Move Out.
The following steps are required for a DMA Read operation:
1. Configure the DMA controller to perform a Read operation. The destination offset will be the offset returned by the VISA Memory Allocation VI.
2. Start the DMA operation. Depending on your DMA controller, you may need to poll some registers or wait for an interrupt to know that the operation is completed.
3. When the DMA transfer completes, the data will be located in the locked memory buffer. Copy the data into a user buffer using the VISA Move In VI with an address space value of VI_PXI_ALLOC_SPACE.
The following steps are required for a DMA Write operation:
1. Configure the DMA controller to perform a ‘Write’ operation. The source offset will be the offset returned by the VISA Memory Allocation VI function.
2. Copy the data from the user buffer into the locked memory using the VISA Move Out VI with an address space value of VI_PXI_ALLOC_SPACE.
3. Start the DMA operation. Depending on your DMA controller, you may need to poll some registers or wait for an interrupt to know that the operation is completed.
Cleaning up after a DMA operation
After all DMA operations have completed all allocated memory must release. Failure to deallocate memory buffers and to close the MEMACC session will result in memory leaks.
The following steps are required for cleaning up a DMA operation:
1. Call the VISA Memory Free to deallocate the memory
2. Call the VISA Close VI to close the VISA MEMACC session.