FPGA’s can handle simple things well, and they handle simple things fast. Other things like PI (3.141592653…) are harder to deal with. How then shall phase be dealt with within an FPGA, and in what units?
The mathematicians will define phase in units of Radians, with values ranging between zero and 2PI. Further, the units are periodic, so anything outside of the range between zero and 2PI may be brought into the range by adding as many 2PI values as necessary.
Others will define phase as a number between 0 and 360 degrees.
Both refer to the same thing–the measure of time within a periodic waveform from the beginning of the period.
While Radians may be the natural units of phase (angles), they are not the first choice of the FPGA designer. Why not?
FPGA’s need to have a known number of bits to represent something. If you allocate enough bits to handle 0 to 8 (containing 0 to 2PI, or 6.28..), you’ll have a difficult time dealing with 16PI.
Radians have roll over problems. While both PI and 3PI/2 are valid angles between 0 and 2PI, their sum is outside of the range between 0 and 2PI. Before you can use this out-of-range value, and before you can add any more phase increments to it, the number must be normalized by subtracting 2PI from it to get it back into the range of 0 to 2PI.
This will cost you a clock, and slow any algorithm you are building down by a factor of two.
It will also only work if your wander into the range between 2PI and 4PI, but not between 50PI and 52PI.
FPGA’s can’t really handle numbers between 0 and 1.
This problem is so common and the solution so well known that aspiring FPGA DSP engineers might just multiply the values from 0 to PI by 2^N and consider that the solution without ever thinking twice.
While these might seem like viable solutions, a better approach to dealing with angles exists.
A Better Unit for Angles
A simpler and better angular unit choice is simply to map the range from 0 to 2PI onto the integer interval from 0 to 2^N-1. This can then be represented with an N-bit number.
fpga_val = floor( theta * 2^N / (2PI) + 0.5)
What’s neat about this approach to angles within an FPGA is that rollover is handled naturally, with no other logic required.
Consider how one might add PI to 3PI/2. We’ll use N=8, and so we’ll be representing angles with to within a degree or two. Hence, our expression becomes 8’h80 plus 8’hc0. If you add these two values together, and throw out any rollover, you get the value 8’h40.
Notice from this example how we didn’t need to waste a clock to bring our results back within limits. Neither were we restricted to only integer Radian values, even though we did our calculation with integers.
Even better, FPGA’s are often optimized for integer arithmetic. Hence, angular adjustments made in this fashion become easy and simple.
Generating Sine or Cosine
Depending upon your needs, you can easily convert this angle to a sine and cosine value.
For example, you could use the top bit as your sine wave value, and get a square wave out with no more work. This was the approach used by the wishbone controlled FM transmitter hack in order to transmit an FM signal from an FPGA.
You could also use the top several bits as an index into a table, and so build a direct digital synthesizer.
Even slicker is to use your angle as an input to a CORDIC algorithm. In that case, you can use this angle as an input to generate a sine wave or cosine wave. You can also use the CORDIC algorithm to multiply by sine and cosine (at the same time), or take arc-tangents. If you like this idea, you ought to know that the CORDIC algorithm can be pipelined so as to operate at one input/output per clock–with no throughput loss, such as you would’ve had were you using either Radians or degrees as your angular units.
Gosh, the CORDIC algorithm is just so useful, we might need to write a post on it by itself.
Coming back to our topic, though, all of these approaches to generating sine waves work nicely … but only if you use the right angular units.
And they say unto him, We have here but five loaves, and two fishes. He said, Bring them hither to me. (Matt 14:17-18)"