The second component of any LabVIEW FPGA application is to create an Application Programming Interface (API) for the FPGA VI. In the LabVIEW Real-Time Module (or LabVIEW for Windows), there is a small set of functions that you use to access the FPGA: Open FPGA Reference, Read/Write Control, Invoke Method, and Close FPGA Reference. These functions provide all of the necessary control over the FPGA, such downloading, reading or writing data, and starting or stopping hardware execution. Integrating an FPGA VI into an application using these functions alone, however, can make your application hard to read, hard to scale, and hard to maintain. Using these low-level functions as building blocks, however, you can create a set of subVIs which can function as an easier-to-use high-level API for use on your host.
There are several benefits to creating an API for your LabVIEW FPGA code. One major benefit that an API provides is abstraction, which involves grouping low-level code into logical functions or data stores. For example, one of the first things to do in an application is initialize your software and hardware. The process of initialization may be several steps of low-level code, but can be easily grouped into one subVI:
Figure 6. Abstraction of the Initialize Function in the FPGATrig_Initialize VI Block Diagram
Creating the API greatly increases the usability of your FPGA VI by hiding the low-level functionality and increasing modularity of the code. Once the subVIs are created, you can simply place them into any LabVIEW Real-Time or LabVIEW for Windows application as functional nodes. In our example, we have created 5 subVIs shown in Figure 7: Initialize, Triggering, Start, Read, and Clear.
Figure 7. Block Diagram of TopLevelAcquisitionDemo VI
Each subVI performs one function and together they form an easy to read and maintain application. Documentation is also easier in the example above. Using good icons and VI documentation in an API greatly improves the readability of the code. Refer to the LabVIEW Help to learn how to create icons and document VIs.
There are several architectural considerations when creating an API. It is good LabVIEW programming architecture to have three main components to any application: Open or Initialize, Operations (Reading or Writing), and Close. For the FPGA Trigger Demo example, we have created this architecture by making an initialize VI, three ‘operations’ VIs, and a Clear VI. The glue that binds each subVI together is the FPGA VI Reference, which is obtained from the Open FPGA Reference function. You can pass this reference into and out of subVIs to create an easy-to-use interface that matches many other common LabVIEW APIs, such as DAQmx, File I/O, Report Generation, etc. In order to keep the FPGA VI Reference consistent amoung subVIs, bind the output of the Open FPGA Reference to a type definition and use this type definition in the subVIs. Another key component to an API is good error handling. By passing the error cluster from one subVI to the next, you make your program more robust and add data dependency between subVIs.
There are several points to highlight in the FPGA Trigger Demo example. In the Initialize VI (FPGATrig_Initialize VI), we call the Open FPGA Reference and the Invoke Method functions to obtain a reference to the VI and to ensure that the FPGA VI is downloaded to the target. The Triggering VI (FPGATrig_PolymorphicTrigger VI) is a polymorphic VI which calls one of the three individual triggering cases depending on the type of triggering desired (FPGATrig_DigitalTrigger VI, FPGATrig_AnalogEdgeTrigger VI, and FPGATrig_AnalogWindowTrigger VI). Each of these triggering VIs on the host configures the triggering application on the FPGA, by reading and writing to the FPGA registers using the Read/Write Control function as shown in Figure 8. The remaining two triggering methods are identical to Figure 8, only the control values are different for each triggering type.
Figure 8. Block Diagram of FPGATrig_AnalogEdgeTrigger VI
The Start VI (FPGATrig_Start VI) simply runs the FPGA VI when it is called by the Run method. Once the VI is running, the Read VI (FPGATrig_Read VI) is called inside of a loop to read the data from an analog input module. The Read VI waits for the trigger, checks for a timeout, and then outputs the data to the TopLevelAcquisitionDemo VI. Another important feature of the Read VI is that it generates an error if there is a timeout. Finally, the Clear VI (FPGATrig_Clear VI) aborts the FPGA VI and calls the Close FPGA Reference function.
With a well thought out and executed API, a programmer can create ‘toolkits’ for their FPGA VIs that can be easily implemented into several larger programs.