1. Arbitrary Waveform Description
An arbitrary waveform is a user-defined waveform that must be specified point-by-point. Because of this, arbitrary waveforms provide nearly limitless flexibility. An arbitrary waveform can be of any shape within the restrictions of the hardware that is generating the signal. These restrictions include horizontal and vertical resolutions and the clock update rate. Since arbitrary waveforms are defined point-by-point, the more update points that define the waveform, the higher the resolution of the output signal. Below is a custom-created arbitrary waveform that is a concatenation of sine, square, triangle, and sawtooth waveforms. Each waveform segment has 100 update points for a total of 400 points.
Figure 1. Arbitrary Waveform
2. Arbitrary Waveform Applications with CompactRIO
By using arbitrary waveforms, engineers and scientists are able to generate unique waveform signals that are specific to their applications. Most often, arbitrary waveforms are designed to simulate “real world” signals. It is possible to integrate glitches, drift, noise and other anomalies on an arbitrary waveform that a device under test will encounter when it leaves the lab or manufacturing floor. Arbitrary waveform generation is used in a wide variety of applications across multiple industries including transducer simulations, disk drive testing, serial data communication, IF modulation testing, anti-lock braking, and engine control.
CompactRIO gives engineers and scientists a customizable, rugged, and portable solution for arbitrary waveform generation. The LabVIEW FPGA Module, used to program the CompactRIO hardware, provides maximum flexibility for the developer. As an example, for arbitrary waveform generation developers are able to customize their hardware so they have the ability to continuously change the update rate, gain, and waveform type as the program runs. Also, developers can customize their hardware triggering to suit the needs of their specific application. In addition, the LabVIEW FPGA Module gives developers the ability to easily program the timing and synchronization of FPGA functions on the same CompactRIO hardware. This is beneficial if an application requires the tight synchronization of arbitrary waveform generation with other FPGA functionality such as digital I/O and analog input. These are just a few of the benefits of using CompactRIO to generate arbitrary waveforms.
3. Arbitrary Waveform Generation Using FPGA Memory
There are multiple methods for programming an arbitrary waveform generator using the LabVIEW FPGA Module. The method that is used depends on the application’s needs and functionality requirements. One method is to take advantage of the 16 KB FPGA onboard memory. By using the FPGA memory a developer can avoid the use of arrays in the FPGA code which can consume a large number of limited FPGA gates. By avoiding arrays, and having additional FPGA gates available, the developer has the ability to write a more complex FPGA program, resulting in increased hardware functionality. The FPGA memory also allows the user to load large waveforms with up to 8192 16-bit samples, thus increasing the waveform resolution. This section outlines the creation of an arbitrary waveform generator that uses the FPGA onboard memory. Additionally, it discusses the use of good programming practices to create scalable, maintainable, and robust FPGA code. Refer to the section, Arbitrary Waveform LabVIEW FPGA Example, to download the example program.
Defining Hardware with the LabVIEW FPGA Module
The first step in developing an arbitrary waveform generator is to program and configure the hardware by using the LabVIEW FPGA Module. The requirements of the hardware are as follows:
- Read data from the FPGA memory
- Output data to an analog output channel*
- Allow the user to adjust the waveform frequency and amplitude while continuously outputting signal
Since the FPGA onboard the CompactRIO hardware can only have one VI loaded at a time, we need to integrate all the above functionality into one LabVIEW FPGA VI. To accomplish this we will use a case structure that has two cases. One case writes data to the FPGA memory and the other case reads data from the FPGA memory and outputs the waveform through the cRIO-9263 module. Figure 2 below, from the LabVIEW FPGA VI Read_Write_Memory(fpga), shows the False case of the case structure. The Memory Write VI within this case allows us to write one data value at a specified address each time Read_Write_Memory(fpga) is called. To write multiple waveform data points into the FPGA memory we must call Read_Write_Memory(fpga) multiple times. This allows us to avoid using arrays to pass data into the LabVIEW FPGA VI. Remember, arrays occupy gates on the FPGA that may be needed for additional hardware functionality as the FPGA VI grows in complexity.
Figure 2. False Case of FPGA VI: Writing Waveform Elements Into FPGA Memory
Figure 3 shows the True case of the Read_Write_Memory(fpga) VI. This case reads each data element of the stored waveform and outputs the corresponding voltage to an analog output channel at a specified update rate. Within this case there are three additional LabVIEW structures, a While Loop, For Loop, and Sequence structure.
Let's first take a look at the Sequence structure which has two sequences. The first sequence has a Loop Timer that can be configured in microseconds, milliseconds, or tick counts. We have specified microseconds in our example, giving us exact hardware timing to control the update rate at which each data point of the waveform is output. The Update Period control feeding the Loop Timer allows us to change the microsecond interval between each data point as the waveform is being generated. The cRIO-9263 has a minimum update period of 3 microseconds when outputting data to one analog output channel. This corresponds to a maximum update rate of 333,333 updates/sec. The second sequence of the Sequence structure has a Memory Read VI followed by a gain multiplication and an Analog Output function. The Memory Read VI has an input called Address and every time the VI is called it reads one data value in the FPGA memory at the specified address. The data is then multiplied by the integer control Gain, and the resulting value is passed to the Analog Output function. By using the Configure Analog Output dialog box associated with the Analog Output function, an analog output channel can be specified. In our case we are outputting the waveform to analog output channel 0 of the cRIO-9263 module. Every time this Sequence structure executes it outputs one data point to the analog output channel list in the Analog Output function.
The next structure is a For Loop that repeatedly calls the Sequence structure and outputs every data element in the waveform. The For Loop iterates a fixed number of times according to the control # Waveform Elements. The iteration terminal of the For Loop tells the Memory Read VI what address to read on the current loop iteration. For example, if there are 100 elements stored in the FPGA memory then the # Waveform Elements control would be set to 100 and the iteration terminal would start at the integer 0. The For Loop would run for 100 iterations, reading FPGA memory addresses 0-99 and output the data values stored at each address space. Additionally, each time the For Loop iterates it reads the two controls within the Sequence structure: Update Period and Gain. This allows the user to change the values of these controls and adjust the waveform as it is being continuously generated.
The final structure in the True case is the While Loop. This allows the waveform generation to repeat continuously until the Stop button is pressed. If the user does not want continuous generation then they can simply take out the While Loop and the waveform will be generated once.
Figure 3. True Case of FPGA VI: Reading Waveform Elements and Outputting
Through the LabVIEW FPGA VI Read_Write_Memory(fpga) described above we are successfully able to fulfill all the hardware requirements and configure the FPGA. The program is able to write data to the FPGA memory, read data from the FPGA memory, output data to an analog output channel, and allow the user to continuously adjust the frequency and amplitude of the signal.
NOTE: Voltage Output with CompactRIO Analog Output Modules
The LabVIEW FPGA Module utilizes integer math for all mathematical operations; therefore, analog output operations with CompactRIO must utilize the binary code representation of the desired output voltage. The binary code representation of the desired voltage is determined by dividing the voltage by the code width of the module. The code width (or step size) is defined as the smallest change in voltage that the system can output. Code width is calculated according to the following equation:
For instance, the cRIO-9263 analog output module is a 16-bit analog output module with a range of –10 V to +10 V. The code width of the module is .305176mV/step (20V / 2^16 steps). Therefore, to output 5.0 volts, you would need to divide 5.0 V by .305176mV. This division yields a binary value of 16384.
National Instruments has included a VI for each CompactRIO analog output module that does this conversion. This VI is named Convert to Binary (cRIO-xxxx), where xxxx is the model number of the module. It is included in the cRIO-xxxx Support Files LLB located in the \examples\FPGA\CompactRIO\cRIO-xxxx directory. This VI converts output voltage values into the binary code representation of that voltage through the following equation:
Configuring the Host Interface
The second step of developing a LabVIEW FPGA application is to create a host interface VI to communicate with the FPGA VI downloaded to the hardware target. In the LabVIEW Real-Time Module (or LabVIEW for Windows), there is a small set of functions for accessing 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 as downloading, reading or writing data, and starting or stopping the VI. 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. By using these low-level functions as building blocks, however, you can create a set of subVIs which can function as an Application Programming Interface (API) for use in your host VI.
There are several benefits for creating an API for your LabVIEW host VI. One major benefit that an API provides is abstraction, which involves grouping low-level code into logical functions or data stores. 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 modules (subVIs) are created, you can simply place them into any LabVIEW Real-Time or LabVIEW for Windows application as functional nodes. In our example, shown in Figure 4, we have created five modules as subVIs: Open, Load, Start, Set Controls, and Clear.
Figure 4. Top-Level VI -> Arbitrary_Output_Memory(Host)
This modular API allows us to easily modify our arbitrary waveform VI and also create new host VIs that need the same functional building blocks. Let's investigate each of the five subVIs that are contained in our host VI, Arbitrary_Output_Memory(Host).
Opening and Closing the FPGA Hardware Execution Reference
Figure 5 shows the Open and Clear VIs of the API. FPGA_Arb_Open_Ref simply opens a Hardware Execution Reference, which is obtained from the Open FPGA Reference function. This Hardware Execution Reference is then used throughout the host VI to specify the hardware target and corresponding FPGA VI that is loaded to that target. FPGA_Arb_Clear is called at the end of the host VI and stops the FPGA VI that is currently running on the target hardware and closes the Hardware Execution Reference.
Figure 5. Opening and Closing the FPGA Reference
Loading the Arbitrary Waveform to FPGA Memory
FPGA_Arb_Load, displayed in Figure 6, allows us to load an arbitrary waveform into FPGA memory. Before going into the details and functionality of this VI we should first discuss different methods to create an arbitrary waveform. The Waveform Input control that sends the arbitrary waveform into FPGA_Arb_Load is a dynamic data type that is converted to a 1-D array of doubles. This gives developers the flexibility to generate and read a waveform from a variety of different formats. In our example we use the Read LabVIEW Measurement File Express VI which allows us to read comma or tab-delimited spreadsheet files that can be easily created in Microsoft Excel. By using the File I/O functions in LabVIEW, developers can also read ASCII and binary files. A great tool that allows the developer to easily create and edit an arbitrary waveform is the National Instruments Analog Waveform Editor. This is an interactive software tool with built-in functionality to create new arbitrary waveforms and also allows the developer to edit existing waveforms that are saved in binary or ASCII formats. Finally, if the developer does not want to read a waveform from a file they can always generate an arbitrary waveform array in LabVIEW.
Once an arbitrary waveform is created or read from a file, it then needs to be loaded to the FPGA memory. Remember from our LabVIEW FPGA VI, Read_Write_Memory(fpga), we have to continuously call the false case to write multiple data values into memory. To do this we use a For Loop that calls a Read/Write Control function and Invoke Method on each iteration. The Read/Write Control function allows us to update the two controls, Address and Data, on the FPGA VI. After updating these controls, we use the Invoke Method function and specify the action as Run to execute the FPGA VI on our target hardware. Each time the For Loop iterates it writes a data value from our Waveform Input array to the FPGA memory at the address specified by the iteration terminal. By using the LabVIEW Array Size function we are able to set the loop count terminal on the For Loop. The subVI Convert to Binary (cRIO-9263) VI takes an input voltage, ±10 V, and converts it to the binary code, –32768 to +32767, that the cRIO-9263 analog output module can output.
Figure 6. Loading the Arbitrary Waveform to FPGA Memory
Initializing and Generating the Arbitrary Waveform
FPGA_Arb_Start, displayed in Figure 7, initializes controls on the LabVIEW FPGA VI, Read_Write_Memory(fpga), and then runs the FPGA VI in the true case. By using the Read/Write Control function we are able to set the initial values of the two controls, Update Period and # Waveform Elements. Once these controls are set, we specify the Run action with the Invoke Method function and the code runs continuously in the true case of the FPGA VI. Remember that this true case continuously reads data from the FPGA memory and outputs the corresponding voltages on a specified analog output channel of the cRIO-9263 analog output module.
Figure 7. Starting the FPGA VI on the Target Hardware
Continuously Updating Waveform Parameters
Once the arbitrary waveform is being generated, we can call FPGA_Arb_Set_Cntls, displayed in Figure 8, to continuously adjust the frequency and amplitude. This is accomplished by using the Read/Write Control function to write the values of the two controls, Update Period and Gain, to the FPGA VI being run on the target hardware. By placing this VI in a While Loop we continuously poll these controls and write them to the FPGA VI as they change values. Additionally, a Stop button is also polled in the same fashion which means the user can stop arbitrary waveform generation as needed.
Figure 8. Updating the Controls on the FPGA VI
CompactRIO gives engineers and scientists a customizable, rugged, and portable solution for arbitrary waveform generation. The LabVIEW FPGA Module, used to program the CompactRIO hardware, provides maximum flexibility for the developer. By taking advantage of the FPGA memory through the advanced Memory Read and Memory Write FPGA functions the developer can create a customized arbitrary waveform generator. The arbitrary waveform generator that we outlined throughout this tutorial can be downloaded below. For additional information about waveform generation with the LabVIEW FPGA Module and CompactRIO return to the main tutorial, Waveform Generation with CompactRIO.
Waveform Generation with CompactRIO