Generating sine using cordic algorithm

1k views Asked by At

I want to apologize for this + - popular question, but nowhere did I find a specific implementation on vhdl. I write the algorithm from scratch and I have a problem with math implementation. The output is invalid. nothing counts, but just shows 1 value. If someone knows what i need to do, how to fix it, would be very grateful for any help.

Math part

    library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.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 primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity massive is
    port ( 
                clk         : in std_logic;
                reset   : in std_logic;
                sinus   : out std_logic_vector (15 downto 0));
end massive;

architecture Behavioral of massive is

type my_type is array (0 to 16) of signed (15 downto 0);
signal x : my_type;
signal y : my_type;
signal z : my_type;
signal j : my_type := ("1010111111001000", "0110011111000101", "0011011011010100", "0001101111010101", "0000110111111000", 
                                                                    "0000011011111110", "0000001101111111", "0000000111000000", "0000000011100000", "0000000001110000",
                                                                     "0000000000111000", "0000000000011100", "0000000000001110", "0000000000000111", "0000000000000100",
                                                                      "0000000000000010", "0000000000000001");
begin

process(clk)

begin
x(0) <= "0000010100000110";
y(0) <= "0000000000000000";
z(0) <= "0000000000000000";
    if rising_edge(clk) then
        if reset <= '1' then
    For n in 0 to 15 loop
        if (z(n) >= 0) then 
            x(n+1) <= x(n) - (y(n)/2**n);
            y(n+1) <= y(n) + (x(n)/2**n);
            z(n+1) <= z(n) + j(n);
        else
            x(n+1) <= x(n) +(y(n)/2**n);
            y(n+1) <= y(n) -(x(n)/2**n);
            z(n+1) <= z(n) - j(n);
        end if;
    end loop;
            sinus <= std_logic_vector(y(16));
    end if;
end if;     
end process;
end Behavioral;

Rotation part

  entity step_control is
    generic (
            first : integer := 0;                           
            second : integer := 1;
            third : integer := 2;
            fourth : integer := 3;                              
    );
    Port ( clk : in  STD_LOGIC;                                 
           Angle : out  STD_LOGIC_VECTOR (12 downto 0);         
           quarter_in : out  STD_LOGIC_VECTOR (1 downto 0));    
end step_control;

architecture Behavioral of step_control is
    signal Ang : std_logic_vector (12 downto 0) := (others => '0');
    signal state : unsigned (1 downto 0) := to_unsigned(first,2);
    signal count_ang : std_logic_vector (11 downto 0) := (others => '0');
begin
process (clk)
begin
    if (rising_edge(clk)) then
        case(state) is
            when to_unsigned(first,2) => if (count_ang >= 3999) then        --00
                            state <= to_unsigned(second,2);
                            count_ang <= "000000010000";
                            quarter_in <= "01";
                            Ang <= Ang - 16;
                        else 
                            state <= to_unsigned(first,2);
                            quarter_in <= "00";
                            Ang <= Ang + 16;
                            count_ang <= count_ang + 16;
                        end if;
            when to_unsigned(second,2) => if (count_ang >= 3999) then       --01
                            state <= to_unsigned(third,2);
                            count_ang <= "000000010000";
                            quarter_in <= "10";
                            Ang <= Ang + 16;
                        else
                            state <= to_unsigned(second,2);
                            quarter_in <= "01";
                            Ang <= Ang - 16;
                            count_ang <= count_ang + 16;
                            end if;
            when to_unsigned(third,2) => if (count_ang >= 3999) then
                            state <= to_unsigned(fourth,2);
                            count_ang <= "000000010000";
                            quarter_in <= "11";
                            Ang <= Ang - 16;
                        else
                            state <= to_unsigned(third,2);
                            quarter_in <= "10";
                            Ang <= Ang + 16;
                            count_ang <= count_ang + 16;
                            end if;
            when to_unsigned(fourth,2) => if (count_ang >= 3999) then
                            state <= to_unsigned(first,2);
                            count_ang <= "000000010000";
                            quarter_in <= "00";
                            Ang <= Ang + 16;
                        else
                            state <= to_unsigned(fourth,2);
                            quarter_in <= "11";
                            Ang <= Ang - 16;
                            count_ang <= count_ang + 16;
                            end if;
            when others => count_ang <= (others => '0');
        end case;
    end if;
end process;
Angle <= Ang;
end Behavioral;

And testbench (but I do not know, I'm kind of all asking in the module. and my "empty" tesbench is obtained)

ENTITY testmass IS
END testmass;

ARCHITECTURE behavior OF testmass IS 

    -- Component Declaration for the Unit Under Test (UUT)

    COMPONENT massive
    PORT(
         clk : IN  std_logic;
         reset : IN  std_logic;
         sinus : OUT  std_logic_vector(15 downto 0)
        );
    END COMPONENT;


   --Inputs
   signal clk : std_logic := '0';
   signal reset : std_logic := '0';

    --Outputs
   signal sinus : std_logic_vector(15 downto 0);

   -- Clock period definitions
   constant clk_period : time := 10 ns;

BEGIN

    -- Instantiate the Unit Under Test (UUT)
   uut: massive PORT MAP (
          clk => clk,
          reset => reset,
          sinus => sinus
        );

   -- Clock process definitions
   clk_process :process
   begin
        clk <= '0';
        wait for clk_period/2;
        clk <= '1';
        wait for clk_period/2;
   end process;


   -- Stimulus process
   stim_proc: process
   begin        
      -- hold reset state for 100 ns.
      wait for 100 ns;  

      wait for clk_period*10;

      -- insert stimulus here 

      wait;
   end process;

END;
1

There are 1 answers

1
AudioBubble On

Your question isn't a Minimal, Complete and Verifiable example, it's not verifiable:

Describe the problem. "It doesn't work" is not a problem statement. Tell us what the expected behavior should be. Tell us what the exact wording of the error message is, and which line of code is producing it. Put a brief summary of the problem in the title of your question.

The output is invalid. nothing counts, but just shows 1 value.

What's the one value? When someone attempts to duplicate your problem one thing we see is assertion warnings for each evaluation of z(n) in the process in entity massive:

       if (z(n) >= 0) then 

The issue is subtle as basic to VHDL signals.

You assign values to signals in the process and expect them to be immediately available. That does not occur. No signal is updated while any process has yet to have been resumed and subsequently suspended in the current simulation cycle.

For each simulation time in the projected output waveform (a queue) there is only one entry. Subsequent assignments (which don't occur here) would result in only the last value being queued.

More important is that the future value isn't available in the current simulation cycle.

x, y and z can be variables declared in the process instead:

architecture foo of massive is
    -- not predefined before -2008:
    function to_string (inp: signed) return string is
        variable image_str: string (1 to inp'length);
        alias input_str:  signed (1 to inp'length) is inp;
    begin
        for i in input_str'range loop
            image_str(i) := character'VALUE(std_ulogic'IMAGE(input_str(i)));
        end loop;
        return image_str;
    end function;
begin

    process (clk)
        type my_type is array (0 to 16) of signed (15 downto 0);
        variable x:  my_type;
        variable y:  my_type;
        variable z:  my_type;
        constant j:  my_type := ("1010111111001000", "0110011111000101",
                                 "0011011011010100", "0001101111010101",
                                 "0000110111111000", "0000011011111110",
                                 "0000001101111111", "0000000111000000",
                                 "0000000011100000", "0000000001110000",
                                 "0000000000111000", "0000000000011100",
                                 "0000000000001110", "0000000000000111",   
                                 "0000000000000100", "0000000000000010",
                                 "0000000000000001");
    begin
        x(0) := "0000010100000110";
        y(0) := "0000000000000000";
        z(0) := "0000000000000000";
        if rising_edge(clk) then
            if reset = '0' then  -- reset not driven condition was <=
                report "init values:" & LF & HT &
                       "x(0) = " & to_string(x(0)) & LF & HT &
                       "y(0) = " & to_string(y(0)) & LF & HT &
                       "z(0) = " & to_string(z(0));
                for n in 0 to 15 loop
                    if z(n) >= 0 then 
                        x(n + 1) := x(n) - y(n) / 2 ** n;
                        y(n + 1) := y(n) + x(n) / 2 ** n;
                        z(n + 1) := z(n) + j(n);
                    else
                        x(n + 1) := x(n) + y(n) / 2 ** n;
                        y(n + 1) := y(n) - x(n) / 2 ** n;
                        z(n + 1) := z(n) - j(n);
                    end if;
                    report  "n = " & integer'image(n) & LF & HT &
                           "x(" & integer'image(n + 1) & ") = " & 
                                   to_string(x(n + 1)) & LF & HT &
                           "y(" & integer'image(n + 1) & ") = " & 
                                   to_string(y(n + 1)) & LF & HT &
                           "z(" & integer'image(n + 1) & ") = " & 
                                   to_string(z(n + 1));
                end loop;
                sinus <= std_logic_vector(y(16));
                report "sinus = " & to_string(y(16));
            end if;
        end if;     
    end process;
end architecture foo;

The report statements are added to allow the values to be output to the simulation console. Without the passage of time between successive assignments to variables values of variables in waveform have no useful meaning. There are simulators that won't report variables in waveform dumps.

And the above architecture produces:

ghdl -a testmass.vhdl
ghdl -e testmass
ghdl -r testmass
testmass.vhdl:86:17:@5ns:(report note): init values:
  x(0) = 0000010100000110
  y(0) = 0000000000000000
  z(0) = 0000000000000000
testmass.vhdl:100:21:@5ns:(report note): n = 0
  x(1) = 0000010100000110
  y(1) = 0000010100000110
  z(1) = 1010111111001000
testmass.vhdl:100:21:@5ns:(report note): n = 1
  x(2) = 0000011110001001
  y(2) = 0000001010000011
  z(2) = 0100100000000011
testmass.vhdl:100:21:@5ns:(report note): n = 2
  x(3) = 0000011011101001
  y(3) = 0000010001100101
  z(3) = 0111111011010111
testmass.vhdl:100:21:@5ns:(report note): n = 3
  x(4) = 0000011001011101
  y(4) = 0000010101000010
  z(4) = 1001101010101100
testmass.vhdl:100:21:@5ns:(report note): n = 4
  x(5) = 0000011010110001
  y(5) = 0000010011011101
  z(5) = 1000110010110100
testmass.vhdl:100:21:@5ns:(report note): n = 5
  x(6) = 0000011011010111
  y(6) = 0000010010101000
  z(6) = 1000010110110110
testmass.vhdl:100:21:@5ns:(report note): n = 6
  x(7) = 0000011011101001
  y(7) = 0000010010001101
  z(7) = 1000001000110111
testmass.vhdl:100:21:@5ns:(report note): n = 7
  x(8) = 0000011011110010
  y(8) = 0000010010000000
  z(8) = 1000000001110111
testmass.vhdl:100:21:@5ns:(report note): n = 8
  x(9) = 0000011011110110
  y(9) = 0000010001111010
  z(9) = 0111111110010111
testmass.vhdl:100:21:@5ns:(report note): n = 9
  x(10) = 0000011011110100
  y(10) = 0000010001111101
  z(10) = 1000000000000111
testmass.vhdl:100:21:@5ns:(report note): n = 10
  x(11) = 0000011011110101
  y(11) = 0000010001111100
  z(11) = 0111111111001111
testmass.vhdl:100:21:@5ns:(report note): n = 11
  x(12) = 0000011011110101
  y(12) = 0000010001111100
  z(12) = 0111111111101011
testmass.vhdl:100:21:@5ns:(report note): n = 12
  x(13) = 0000011011110101
  y(13) = 0000010001111100
  z(13) = 0111111111111001
testmass.vhdl:100:21:@5ns:(report note): n = 13
  x(14) = 0000011011110101
  y(14) = 0000010001111100
  z(14) = 1000000000000000
testmass.vhdl:100:21:@5ns:(report note): n = 14
  x(15) = 0000011011110101
  y(15) = 0000010001111100
  z(15) = 0111111111111100
testmass.vhdl:100:21:@5ns:(report note): n = 15
  x(16) = 0000011011110101
  y(16) = 0000010001111100
  z(16) = 0111111111111110
testmass.vhdl:109:17:@5ns:(report note): sinus = 0000010001111100

Where we see the values of your array elements are changing instead of propagating 'X's through addition (or subtraction when z(n) < 0), assigned in the previous loop iteration.

Also note the reset doesn't change value in the testbench and there is an erroneous evaluation for it's value using the relational operator "<=" in the original massive process.

j is not assigned other than an initial value and is shown as a constant in the above architecture.

I'm personally somewhat skeptical you can perform these 16 chained additions or subtractions along with selecting which operation in one 10 ns clock.