# How can I generate a 100kHz sine wave on FPGA?

I am performing direct digital synthesis on my FPGA using lookup tables, and jumping through them to increase output frequency.

As the value of the phase accumulator increases, the sine wave becomes increasingly distorted, which isn't a problem since I have a low pass filter (not my domain.) The issue is that the output deviation increases heavily.

I have a couple of questions:

1. I understand why the distortion in the sine wave occurs, but why is there a deviation in output frequency? I developed a formula for my DAC. The deviation is quite a lot. Why does this variation occur?
2. How do I accurately generate 100kHz? My phase accumulator is 32 bits, and my LUT is 10-bit. I can't increase it too much due to memory considerations.

Edit:

Code for LUT:

module LUT_1024(
input clk, rst,
output reg [15:0] new
);

always @(*) begin
if(rst) begin new<=0; end
else begin

10'd0: new<=16'h80C;
10'd1: new<=16'h818;
10'd2: new<=16'h825;
10'd3: new<=16'h831;
.
.
.
10'd1023: new<=16'h80C;
default: new<=16'h0;
endcase
end end
endmodule

• How did you implement the "time" base ? Commented Mar 18 at 7:01
• How did you generated the LUT? Pl share your code for reading the LUT as well. Commented Mar 18 at 7:32
• Which FPGA you are using? If you are using Xilinx, then you can use the DDS IP in Vivado and that will generate the desired frequency. Commented Mar 18 at 7:44
• @ImGroot I wish to generate the sine wave manually. I don't want to rely on the IP. Commented Mar 18 at 8:47
• Frequency issues are associated with the DDS logic, not the LUT logic. Show us that code. In fact, you should provide an MCVE, as well as explain exactly what the difference is between what you expect to happen and what actually happens. Commented Mar 18 at 11:58

First, you have to isolate the issue and find out if either the issue is with your Verilog module or with the DAC.

I see a couple of issues in your Verilog module.

1. You have not used clk in the module and you are using the combinational logic to send data to output.
2. You have used Non-blocking statement (<=) in the always block.

To correct it, you have to use the clk in the sensitivity list.

module LUT_1024(
input clk, rst,
output reg [15:0] new
);

always @(posedge clk) begin
if(rst) begin
new<=0;
end
else begin

10'd0: new<=16'h80C;
10'd1: new<=16'h818;
10'd2: new<=16'h825;
10'd3: new<=16'h831;
.
.
.
10'd1023: new<=16'h80C;
default: new<=16'h0;
endcase
end
end
endmodule


But this is not a recommended way to work with LUT in Verilog. To create a LUT table we define a memory and provided values in the initial block. And then rest of the time we just read from the memory, as below.

module LUT_1024(
input clk, rst,
output reg [15:0] new
);

reg [15:0] LUT_Mem [0:1023];

initial begin
LUT_Mem[0]=16'h80C;
LUT_Mem[1]=16'h818;
LUT_Mem[2]=16'h825;
LUT_Mem[3]=16'h831;
.
.
.
LUT_Mem[1023]=16'h80C;
end

always @(posedge clk) begin
if(rst) begin
new<=0;
end
else begin
end
endmodule


Further issue that you have to look for is your DAC is 12 bit but the LUT values that you are generating are going to exceed the 12 bit. For example, the maximum value that should be given to DAC has to be less 4096 but the LUT that you have generated seems to exceed that value based on the starting value that you have provided i.e. 16'h80C.

Generate the LUT with the following settings from the link:

Number of Points: 1024
Maximum Amplitude Value: 4000
Numbers Per Line: 1
Output Values Type: Hex


Now there is one more thing that you have to check for is, how you are generating the address to read values from the LUT table in the above Verilog module. If you are not generating address properly then it may de-shape the sin wave.

Lastly, simulation is your ally here. If the error is on FPGA side (i.e. Verilog) then you can easily capture it in the simulation.

• For the LUT format, I will attempt it. Secondly, the values are less than 4096, since anything beyond that given to the DAC will lead to a shut-down mode. That doesn't occur in the output waveform, at least from what I can observe. Lastly, the address is calculated by a control parameter, which when increased will step through the LUT. It doesn't go beyond the domain of the LUT using limits that I have applied. So I don't really see where I am going wrong. Commented Mar 18 at 10:20
• The issue for the address is not going beyond the limits, it how you are incrementing the values of the address and if the interval is correct or not. Commented Mar 18 at 12:11
• Generating a frequency is a function of multiple parameter i.e. Correct LUT, Correct increments and Accurate interval after which next value is sent to output. Slight change in the interval and the result will be different Commented Mar 18 at 12:12
• As per your issue, I would start by simulating the LUT with read module and verify if correct tone is being generated or not. Next, I will place an ILA (debug) core and check if correct tone is being sent to output at runtime or not. If tone is correct then I would look into how I am giving data to the DAQ? Commented Mar 18 at 12:15