Multiplying, Dividing and Scaling in LabVIEW FPGA
Overview
Engineers developing control and simulation applications using LabVIEW FPGA often need to scale values directly in the FPGA code to generate signals or implement a control algorithm. Using the FPGA Saturation Math and Scaling functions you can create code to perform non-integer multiplication and division routines that outperform the standard Quotient and Remainder function.
Table of Contents
Overview
Data processing on the FPGA is required in a wide range of applications, including sensor measurements and simulation, closed-loop control and many others. Compared to floating/fixed point processor operations that we are used to on platforms such as Windows and LabVIEW Real-Time, the integer math routines available on the FPGA make data processing a little more challenging.
Integer math functions can be used to scale or multiply data by whole integer values. Bit shifting can be used to multiply or divide a value by any power of two. When we combine these two operations we create a simple method to scale or multiply a value by a non-integer scaling constant.
A common application of scaling an input value by a constant are simulators of sensors such LVDTs and synchro/resolvers. Each of these sensors has an excitation voltage input which is fed with a sinewave signal. The sensor modulates the amplitude of the excitation signal based on the position of the sensor and the resulting output signal is passed to the measurement system. When we want to simulate such a sensor we need to measure the excitation signal and then generate an analog output value that corresponds to the excitation voltage scaled by a value corresponding to the position of the simulated sensor. This operation requires us to quickly multiply the analog input measurement by a non-integer value and generate the result on an analog output channel.
Implementation
One method to handle scaling in LabVIEW FPGA is to use a Multiply function followed by a Scale by Power of 2 function.
We first take the input value and multiply it by a known integer, generating a larger intermediate result. The intermediate result is bit shifted to the left (Scale by Power of 2 with a negative n value) which effectively is a division by a power of 2. Combining the multiplication and division gives us the effective scaling or multiplication of the input value by a non-integer value.
Figure 1 below shows an example of this scaling implementation. Note: A Single-Cycle Timed Loop is used to generate optimized LabVIEW FPGA code. This code could be further optimized by replacing the Bit Shift control with a constant.

Figure 1: FPGA VI to scale by a non-integer value
As part of these calculations we need to make sure that the intermediate result of the multiplication will fit in the data type that we are using. With the Saturation Multiply function we can multiply two 16-bit integers, generate a 32-bit value, and be sure that the result will fit into the 32-bit integer. If the final result needs to be a 16- bit integer then the Scale by Power of 2 function needs to shift the intermediate product back into the 16-bit range as shown in figure 1 with the coerce function.
The integer multiplier and bit shift value will determine the non-integer scaling value by which we are multiplying the input value.
For example, to scale by 1.5, set the Multiplier to 3 and the bit Shift to -1. This leads to 3/2=1.5.
To scale by 1/7 (~0.1428), set the Multiplier to 73 and the Shift to -9. This leads to 73/512 ~= .1425.
This method of combining a multiplication and Scale by Power of 2 will not give us an exact result for every non-integer scaling value, but it will give us a very good approximation within the limited resolution of integers. The key is to find the right combination of Multiplier and Bit Shift value.
For example, to divide by 10, we can use a multiplier of 3 and bit shift of -5 (divide by 32). This will result in an effective scaling constant of 3/32 = 0.09375, an error of 0.00625 from our intended value. However we could also use a Multiplier of 102 and a shift of -10 (divide by 1024). This will give us an effective scaling constant of 102/1024 = 0.09961, an error of 0.00039 from our intended value. In general we will get better results when we use larger multipliers and bit shift values.
Of course as we increase the multiplier we need to make sure we do not exceed the range of our intermediate result data type, otherwise we will saturate this value and receive an incorrect result. To find the right Mutliplier and Bit Shift values, it is often easiest to pick a suitably large Bit Shift value based on the output value range and data types used and then calculate the corresponding Multiplier for your desired scaling value. Also using a constant value for the bit shift will use less resources on the FPGA when compiled.
Table of sample scaling values and corresponding multiplier and bit shift values:
|
Desired Scaling Value
|
Multiplier
|
Bit Shift
|
Actual Scaling Value
|
Absolute Error
|
|
1.5
|
3
|
-1
|
1.5
|
0
|
|
0.1428
|
73
|
-9
|
.1425
|
.0003
|
|
10
|
3
|
-5
|
0.09375
|
0.00625
|
|
10
|
102
|
-10
|
0.09961
|
0.00039
|
See Also:
LabVIEW FPGA Scaling and Division
Calculating Scaling Constants
To automate the process of determining the best multiplier and bit shift value to use, we can write a VI to run on the host platform (Windows or LabVIEW RT) that will calculate these values for a given input range and scaling factor. An example of this VI is included in the referenced example.

[+] Enlarge Image
Figure 2: Host VI to calculate the multiplier and bit shift for a given input range and scaling factor
The VI calculates the multiplier and bit shift value based on the input range and scaling factor. Based on the input values the VI determines the output range and then calculates the maximum possible bit shift without exceeding a 32-bit integer for the intermediate result. Next it calculates the multiplier corresponding to the bit shift and scaling value. Lastly, it determines the actual scaling value for the selected parameters and the relative error to the desired scaling value.
The calculated multiplier and bit shift can be used in the design of the FPGA VI or downloaded at run-time from the host application.
See Also:
LabVIEW FPGA Scaling and Division
Sample Application
The scaling VI shown above can easily be integrated into a wide range of applications that require scaling and multiplying of values on the FPGA. As mentioned earlier a typical example is the simulation of a LVDT sensor where we need to measure the excitation signal and generate a scaled copy of the input on an analog output (figure 3). The 16-bit measurement from the analog input is pipelined and passed to the scalingsub VI. The result of the scaling operation is routed to the analog output.

Figure 3: Sample LVDT simulation application using the scaling VI
The multiplier and bit shift value in this example are set from the host application based on the position of the simulated LVDT sensor. The host VI calculates these parameters based on the scaling values which is determined using the LVDT formula.
See Also:
LabVIEW FPGA Scaling and Division
Division
Using the VI and example shown above we can easily scale or multiply an integer by a wide range of non-integer scaling values. We can use the same method to divide an integer using the inverse of the denominator as our scaling value. For example, to divide by 7 you can scale by 1/7 as shown above. This, however, does require that we know the denominator value when we are developing the FPGA VI or that we have the denominator in our host application. In the host application we can take the inverse of the denominator and then calculate the multiplier and bit shift values to download to the FPGA,
This process does not allow us to easily divide a value on the FPGA by another dynamic value on the FPGA, because we can not directly take the inverse of a value on the FPGA. Using the Quotient and Remainder function or a loop-based subtraction routine are two possible solutions for a division routine on the FPGA.
The loop based subtraction routine (figure 4) continuously subtracts the denominator value from the numerator until it reaches zero. It can round the quotient to the next higher value based on the remainder and denominator value to give a more accurate result. The large drawback in this solution is that this routine requires a variable amount of execution time based on the numerator and denominator values and the timing behavior becomes indeterministic. Depending on the range of values and the timing requirements of your application this may be an acceptable solution.

[+] Enlarge Image
Figure 4: Loop based division algorithm in LabVIEW FPGA
See Also:
LabVIEW FPGA Scaling and Division
Summary
While the FPGA does not support floating point math operations, we have seen that using the available integer math functions and some extra logic we can implement approximations for a variety of applications and are able to implement data processing code directly on the FPGA.
Reader Comments | Submit a comment »
Legal
This tutorial (this "tutorial") was developed by National Instruments ("NI"). Although technical support of this tutorial may be made available by National Instruments, the content in this tutorial may not be completely tested and verified, and NI does not guarantee its quality in any way or that NI will continue to support this content with each new revision of related products and drivers. THIS TUTORIAL IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND AND SUBJECT TO CERTAIN RESTRICTIONS AS MORE SPECIFICALLY SET FORTH IN NI.COM'S TERMS OF USE (http://ni.com/legal/termsofuse/unitedstates/us/).
