Home
Welcome
Information


FPGA projects
Basic
Music box
LED displays
Pong game
R/C servos
Text LCD module
Quadrature decoder
PWM and one-bit DAC
Debouncer
Crossing clock domains
External contributions

Interfaces
RS-232
JTAG
I2C
EPP
SPI
PCI
PCI Express
10BASE-T

Advanced
Digital oscilloscope
Graphic LCD panel
Direct Digital Synthesis
CNC steppers
Spoc CPU core

Hands-on
A simple oscilloscope


FPGA introduction
What are FPGAs?
How FPGAs work
Internal RAM
FPGA pins
Clocks and global lines
Download cables
Configuration
Learn more

FPGA software
Design software
Pin assignment
Design-entry/HDL
Simulation/HDL
Synthesis and P&R

FPGA electronic
SMD technology
Crystals and oscillators

HDL info
HDL tutorials
Verilog tips
VHDL tips

Quick-start guides
ISE
Quartus

Site
News
FPGA links
HDL tutorials
Forum


Direct Digital Synthesis (DDS) - Interpolation

Now, while the phase accumulator is quite precise, the output suffers from the limited number of entries in the lookup table: the output value "jumps" when going from one entry to the next. This is particularly sensible for low output frequencies, but affects high output frequencies too, and this introduces unwanted high-frequencies in the output spectrum.

We're going to fix that.
To make it easier to understand, let's go back to a 15 bits phase accumulator.

// sine without linear interpolation
reg [14:0] phase_acc;    // 15 bits
always @(posedge clk) phase_acc <= phase_acc + 15'h1;

sine_lookup my_sine(.clk(clk), .addr(phase_acc[14:4]), .value(sine_lookup_output));

The above code moves from one lookup table to the next every 16 clocks. That makes the output "jump" every 16 clocks.

The most effective way to improve that is to use the lowest 4 bits of the phase accumulator (unused until now) to linearly interpolate between two successive lookup table entries. That's pretty easy to do using two lookup tables instead of one.

// sine with linear interpolation
reg [14:0] phase_acc;
always @(posedge clk) phase_acc <= phase_acc + 15'h1;

// use two lookup tables to get two successive table values
wire [16:0] sine1_lv, sine2_lv;  
sine_lookup my_sine1(.clk(clk), .addr(phase_acc[14:4]      ), .value(sine1_lv));
sine_lookup my_sine2(.clk(clk), .addr(phase_acc[14:4]+11'h1), .value(sine2_lv));

// now the 4 LSB bits from the phase accumulator need to be delayed
// (to match the latency introduced by the lookup tables)
reg [3:0] phase_LSB_delay1;  always @(posedge clk) phase_LSB_delay1 <= phase_LSB[3:0];
reg [3:0] phase_LSB_delay2;  always @(posedge clk) phase_LSB_delay2 <= phase_LSB_delay1;
reg [3:0] phase_LSB_delay3;  always @(posedge clk) phase_LSB_delay3 <= phase_LSB_delay2;

// before we can use them to do the interpolation
wire [4:0] sine1_mf = 5'h10 - phase_LSB_delay3;
wire [3:0] sine2_mf = phase_LSB_delay3;
reg [20:0] sine_p; always @(posedge clk) sine_p <= sine1_lv*sine1_mf + sine2_lv*sine2_mf;

assign DAC_data_out = sine_p[20:11];

The two lookup tables contains the same values. We extract a value from one, and its neighbor value from the other (the "phase_acc+1"), so that we can linear interpolate between the two values.

The interpolation allows us to get a better resolution out of the DDS, while keeping the lookup table sizes resonable. Our lookup tables create the sine function with 2048 values. And between each value, we linear interpolate 15 points, so we end-up with 2048*16=32768 sine points, much like having bigger lookup tables. Of course, out of these 32768 points, only 2048 are really coming from the lookups, and the rest (32768-2048=30720) are linearly interpolated.

The final step is to move back to the 32 bits phase accumulator to cover a wide range of frequencies - we'll leave that to you. The complete code is also available in KNJN Saxo-Q's startup-kit.

Ideas of improvement

Your turn to experiment!






This page was last updated on May 24 2010.