VHDL coding tips and tricks: VHDL: Simple Sine Wave Generator with Testbench

Wednesday, March 17, 2010

VHDL: Simple Sine Wave Generator with Testbench

    In this post, I want to share a sine wave generator written in VHDL language. The samples from the sine wave are passed one by one to the output port. All the samples are stored inside the entity in a ROM(implemented using an integer array). 

    The sine wave's amplitude values can be generated from this online tool - Sine Look up table generatorFor this particular version of the code, I used the following settings:



You can generate a sine wave using different settings and paste them in the code given below.

To improve the smoothness and precision of the sine wave use more number of points and a higher value for maximum amplitude value. But remember that if we use too many points, the FPGA resources may not be able to keep up with the size of the ROM needed. So even though its possible theoretically, it may not be practically feasible. 

The design can be further improvised by reducing the size of the ROM to 1/4th of its original size. Since sine wave is symmetric, if we have the values for any one quadrant, then the rest of the quadrants can be figured out. This comes at the expense of a slightly more complicated logic in the code.

If you need further explanation you can watch this Youtube video I have created - Generic Sine Wave Generator (LUT Based) in VHDL

sine_wave.vhd:


--library declarations
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all; --try to use this library as much as possible. entity sine_wave is generic(NUM_POINTS : integer := 32; MAX_AMPLITUDE : integer := 255         ); port (clk :in std_logic; dataout : out integer range 0 to MAX_AMPLITUDE ); end sine_wave; architecture Behavioral of sine_wave is signal i : integer range 0 to NUM_POINTS := 0; type memory_type is array (0 to NUM_POINTS-1) of integer range 0 to MAX_AMPLITUDE; --ROM for storing the sine values generated by MATLAB. signal sine : memory_type :=
    (128,152,176,198,218,234,245,253,  --sine wave amplitudes in the 1st quarter.
    255,253,245,234,218,198,176,152,    --sine wave amplitudes in the 2nd quarter.
    128,103,79,57,37,21,10,2,    --sine wave amplitudes in the 3rd quarter.
    0,2,10,21,37,57,79,103);    --sine wave amplitudes in the 4th quarter.

begin

process(clk)
begin
--to check the rising edge of the clock signal
if(rising_edge(clk)) then    
--one by one output the sine amplitudes in each clock cycle.
dataout <= sine(i);
i <= i+ 1; --increment the index.
if(i = NUM_POINTS-1) then  
    --reset the index to zero, once we have output all values in ROM
    i <= 0;
end if;
end if;
end process;

end Behavioral;

Testbench - tb_sinewave.vhd:


--library declarations
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library std;
use std.env.finish;
 
--entity for testbenches are always empty
entity tb_sinewave is
end tb_sinewave;
 
architecture behavior of tb_sinewave is 
 
--declare the component which we are going to test. Here that is "sine_wave"
component sine_wave is
generic(NUM_POINTS : integer := 32;
        MAX_AMPLITUDE : integer := 255
       );
port(clk :in  std_logic;
    dataout : out integer range 0 to MAX_AMPLITUDE
    );
end component;
    
--generic constants
constant NUM_POINTS : integer := 32;
constant MAX_AMPLITUDE : integer := 255;
    
--Inputs
signal clk : std_logic := '0';
--Outputs
signal dataout : integer range 0 to MAX_AMPLITUDE;
-- Clock period definitions
constant clk_period : time := 10 ns; 
--temporary signals
signal data_out_unsigned : unsigned(31 downto 0);
    
begin
 
-- Instantiate the Unit Under Test (UUT)
uut: sine_wave generic map(NUM_POINTS, MAX_AMPLITUDE)
	port map(clk => clk,
            dataout => dataout
            );

data_out_unsigned <= to_unsigned(dataout, 32);
	
-- Clock process definitions
clk_generate_process :process
begin
    wait for clk_period/2;
    clk <= not clk;
end process;
 
-- Stimulus process
stim_proc: process
begin		
    wait for clk_period*NUM_POINTS*2;
end process;

end;


Note the following about the sine wave generator:
These two questions were raised by readers through comments, let me answer them here:
  1. What is the frequency of the generated sine wave?
    The frequency depends upon the clock frequency supplied to the sine wave entity and the number of points of sine wave stored in the array.
    We can say, freq = frequency_clk / number_of_points;
  2. I cannot see sine wave when I simulate this code. Why?
    Its possible to see the shape of sine wave in modelsim software. I got the following waveform from modelsim. Right click on the signal name and choose the correct settings to display the values in analog format.




36 comments:

  1. hi!

    How do you do to change the frequency of your sine wave?

    Jocelyn

    ReplyDelete
  2. @Jocelyn : The freq of the sine wave is determined by the freq of clk in the above program.Here we need 30 clk cycles for sending one full cycle of sine values.So the period of sine wave is (freq of clk/30).
    Hope I am clear.

    ReplyDelete
    Replies
    1. can u give us a tutorial on using delta-sigma DAC ?

      Delete
  3. heloo jocelyn!

    in the above code i have simulated and runed for 30 sin values but the out put wave does not looks like sin! is their any settings reqired for getting out put wave in the form of sin as the below!

    file://localhost/C:/Documents%20and%20Settings/Administrator/Desktop/enp%20final/Synthesisable%20Sine%20Wave%20Generator_files/wave.gif

    ReplyDelete
  4. @prahlad : I never told that the wave will look like a sine wave in the simulator.If you want to test out use a DAC,interface it with FPGA board and connect the output of DAC to an oscilloscope.

    Or another thing you can do is take a graph sheet draw the output values Vs time.You will get a sine signal.If you still not getting contact me.

    ReplyDelete
    Replies
    1. dude iam using fpga spartan 3e ... i need 2 generate sine wave ,,,,should i use dac and thn fpga to burn code

      Delete
    2. hi vipin can you help me in implementing PWM and random PWM in fpga...

      Delete
  5. really, people probably should be using the coregen dds compiler. but overall, this shows the basic concept.

    A DDS will generally exploit the symmetry in the sine wave -- only 1/4th of the sine wave values are needed.

    this allows a larger table to be used. using a lot of entries allows the user to increment by N, to generate other frequencies.

    ReplyDelete
  6. @Chris : I do agree. But if you just use core gen IP's for each and everything then how will you learn.This is a basic code where you can see how it works.I have mentioned in this post itself that,"The code can be modified for efficient use of memory so that,only the first (pi/2) values are stored in the ROM".
    I just left it as an exercise for readers.

    ReplyDelete
  7. Thanks,
    I need to learn how to use the DAC so I can play with this!!

    ReplyDelete
  8. how to interface the dac with the fpga ?

    ReplyDelete
  9. The values were generated using matlab??? Is thr a formula for d generation of sine values??

    ReplyDelete
  10. hi ,

    i just wanna ask , if i syenthesized this code and , interface it with VGA port , would it display the sinewave figure on monitor ?

    ReplyDelete
    Replies
    1. Absolutely not...You would need to create double buffering and index the buffers and map the pixel data to the RGB signals and sync to the pixel clock...This requires synchronization with the hsync and vsync signals...It is not a simple thing to do and I suggest you hit the books really hard if you want to learn about very advanced HDL controllers...Just because it can be done, it does not mean it is easy to do so...

      Delete
  11. can we get sine wave in modelsim

    ReplyDelete
  12. how can we get sine wave in modelsim?

    ReplyDelete
  13. Yes, you can get a sine wave to show up in modelsim...Just view the data as signal! :)

    ReplyDelete
  14. Yeah, I think somebody mentioned above that this could have been done much better in four states using a quarter wave LUT and four simple states marking each quadrant...This translates 0-90 degrees would have been decimated into a LUT to whatever radians division is desired. Then on the quadrant 90-180 you would read the LUT in reverse going backwards 90-0 by reversing the indexes of the LUT and so on into the other quadrants, but at that point from 180 to 270 and 270 to 360, you'd want to negate the LUT values. That is how a quarter wave LUT works...

    ReplyDelete
  15. Hi there! I have generated sine wave.. But after some clock cycle the wave is not accurate..some samples are missing..I have tried adjusting the frequency but still its distorting.. For example., in the case of FSK the waves are distorting after some clock cycles.. What should I do??

    ReplyDelete
  16. Hi Vipin am working on a similar project but am not using Matlab am only using Quartus Software, how can I generate and store the sine wave values using the Quartus software

    ReplyDelete
  17. The calculation of the sin values is wrong. At 8 bits I would expect the maximum value to be close to 127, however this example only produces 77 as the maximum.
    The correct calculation would be int32(sin(t) * 127) for 8 bit or more general int32(sin(t) * 2^(n-1)) for n bits.

    Also most people don't have MATLAB available so here is some python code:
    n=30;bits=8; from math import sin, pi;print([int(round(sin(x*2*pi/n)*(2**(bits-1)-1))) for x in range(n)])
    "n" is the number of values and "bits" the number of bits per value.
    You can try it online at https://repl.it/BcM6/1

    ReplyDelete
  18. can i generate sinewave using cpld?can i store look up table values in cpld and call it one by one? need help..

    ReplyDelete
  19. can i generate sinewave using cpld? can i store look up values in cpld and can just call it? need help

    ReplyDelete
  20. How to test the code?/ what to write in the testbench

    ReplyDelete
    Replies
    1. you dont need to enter "anything". You just run it. The only stimulation that you need is the clk. Just declare your clk period, your internal clk signal and instantiate the UUT.

      Delete
  21. after writing vhdl code in quartus-ii how to view the output iesinewave in modelsim..?

    ReplyDelete
  22. The code runs perfectly fine ie generates sine wave(i am using vivado software for running the code which allows analog sine wave output.)
    But my question is that whether this sine wave could be used in other programs such as generation of ASK ,PSK by changing its phase and frequency. In other words what will be the code to change its phase and frequerncy?

    ReplyDelete
  23. Can we use floating point numbers in vhdl?

    ReplyDelete
  24. library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
    use IEEE.std_logic_signed.all;

    -- Uncomment the following library declaration if using
    -- arithmetic functions with Signed or Unsigned values
    --use IEEE.NUMERIC_STD.ALL;

    -- Uncomment the following library declaration if instantiating
    -- any Xilinx leaf cells in this code.
    --library UNISIM;
    --use UNISIM.VComponents.all;

    entity lms2 is
    Port (wr9,wi9,xr,xi : in STD_LOGIC_VECTOR (12 downto 0);
    --u : in integer;

    yr,yi,er,ei,wr8,wi8,zzz,sum1 : out STD_LOGIC_VECTOR (12 downto 0));
    end lms2;

    architecture Behavioral of lms2 is
    signal a,b :real;
    --signal yr11, yi11 : real;

    --variable r,s : real range 0.0 to 15.0;

    begin

    process(wr9,wi9,xr,xi)--process also runs without the parameters,but it gives problems during simulation
    --process(u,xr,xi)
    variable wr : std_logic_vector(12 downto 0);
    variable wi : std_logic_vector(12 downto 0);

    --variable wr : std_logic_vector(4 downto 0) :="00010";
    --variable wi : std_logic_vector(4 downto 0) :="00011";
    --variable wr : std_logic_vector(4 downto 0) :=wr9;
    --variable wi : std_logic_vector(4 downto 0) :=wi9;
    variable xr1 : real :=3.2;
    variable xi1 : real :=4.1;
    variable dr1 : real :=5.2;
    variable di1 : real :=4.4;
    variable yr1 : real;
    variable yi1 : real;
    variable er1 : real;
    variable ei1 : real;
    variable wr1 : real := 1.8;
    variable wi1 : real := 2.1;
    variable u : real :=0.2;


    variable f : real := 3.2;
    variable g : real := 2.6;
    variable h : real;
    variable hh : real;

    begin

    h := f+g;
    hh := f*g;
    a <= h;
    b <=hh;

    --process(xr,xi,u)
    --bi <= to_integer(unsigned(k)) ;
    --bj <= to_integer(unsigned(l)) ;
    --bk <= bi*bj;





    for z in 0 to 3 loop

    yr1 := wr1*xr1-wi1*xi1;--line1
    yi1 := wr1*xi1+wi1*xr1;--line2

    er1 := (wr1*xr1)-(wi1*xi1)-dr1;--line3
    ei1 := (wr1*xi1)+(wi1*xr1)-di1;--line4

    wr1 := u*(xr1*er1-xi1*ei1)+wr1;--line5
    wi1 := u*(xr1*ei1+xi1*er1)+wi1;--line6



    end loop;

    --yr11 <= yr1;
    --yi11 <= yi1;

    end process;


    end Behavioral;


    We are implementing LMS algorithm . If we are adding line 5 and line 6 (as indicated by comments), we are getting error right from line 1 (as indicated by comments) that "non constant real valued expression not supported". However when we are omitting line 5 and line 6, we are not getting the error. Please ignore the STD_LOGIC_VECTORS(in and out)added in the beginning, as they are reserved for future use

    ReplyDelete
  25. how to make it suitable for a lvds dac?

    ReplyDelete
  26. what is the frequency of above sine wave??

    ReplyDelete
  27. What is the frequency of above sign wave and how do you modify it?

    ReplyDelete
  28. We are implementing LMS algorithm .

    ReplyDelete
  29. how simulated analog signal using ise

    ReplyDelete