How to "ARCTAN" Function in VHDL

Status
Not open for further replies.
Hi,

I don´t know the VHDL solution.

Often with atan one like to get the direction of a vector. 0...360°.
But atan repeats every 180°. You may need additional logic to get full 360° information.

Klaus
 

That package you posted is part of the VHDL standard, but it is not synthesisable (ie, you cannot use it on a chip). You can use it to calculate constants/generics in your VHDL design at elaboration time.
For trigonometric functions, most people look into using a CORDIC function, or build a look up table (you can use math_real to calculate this).
 


You have to write your own 'code' to calculate atan(x) value. Another possibility is to use vendor specific IP core:
https://www.altera.com/en_US/pdfs/literature/ug/ug_altfp_mfug.pdf
You may want to convert signed/unsigned value into ieee754 format, calculate atan(x) and then again do a conversion from ieee754 into signed/unsigned.
 

Thanks all for replying

I try to apply your given solutions but problem accrue in look up table as well as in CORDIC is there is less space remaning in FPGA board. So I have to cancle that idea.
If am no wrong I can write a code using Taylor Series which is
arctan(x)=x-x^3/3+x^5/5-x^7/7+ ...

I am trying to write code but error accrue here, below code is given. It says that Type of aout is incompatible with type of 'conversion to unsigned'


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;
 
 
entity atan_try is
  port(
    xin : in std_logic_vector(31 downto 0);
    aout : out std_logic_vector(31 downto 0)
    );
end atan_try;
 
architecture Behavioral of atan_try is
 
begin
 
aout <= unsigned (signed(xin) - ((signed(xin)*signed(xin)*signed(xin))/3) + ((signed(xin)*signed(xin)*signed(xin)*signed(xin)*signed(xin))/5) - ((signed(xin)*signed(xin)*signed(xin)*signed(xin)*signed(xin)*signed(xin)*signed(xin))/7));
 
end Behavioral;

 
Last edited by a moderator:

Without looking into the internal calc....
Yes, because aout is of type std_logic_vector and you are type casting the entire calc to unsigned.

Try this:
aout <= std_logic_vector( unsigned (signed(xin) - ((signed(xin)*signed(xin)*signed(xin))/3) + ((signed(xin)*signed(xin)*signed(xin)*signed(xin)*signed(xin))/5) - ((signed(xin)*signed(xin)*signed(xin)*signed(xin)*signed(xin)*signed(xin)*signed(xin))/7)) );
 

Besides using incompatible types, there are problems with data width and result scaling. It's also inappropriate to use division operations for the Fourier parameter scaling.

The result of a simple chained multiply of 7 32-bit numbers has a width of 7*32 bits. You'll define a fixed point interpretation of the 32 bit input number and get a 32 bit result by respective rounding and truncation.

Division should be replaced by a multiply with fixed point constant.

Instead of chained asynchronous multiply, you better perform sequential multiply with x² in a clocked process.
 

This is a generic synthesize tan(x) LUT I designed once.
You can try to do the same with arctan.

Code:
library ieee ;
	use ieee.std_logic_1164.all ;
	use ieee.numeric_std.all ;
	use ieee.math_real.all ;

entity tan_lut is

generic
(
    WIDTH_FRACTION_ANGLE    : natural := 4  ; -- 4 is 1/16
    WIDTH_FRACTION_RESULT   : natural := 10 
) ;

port 
(
	IN_ANGLE    :   in  std_logic_vector ( 7 + WIDTH_FRACTION_ANGLE - 1 downto 0 )  ;   -- 90 is 1011010 ( 7 bits ). A sign bit isn't needed ( the user shall exploit the tan symmetry ).	     
	IN_CLOCK    :   in  std_logic                                                   ;																		    
																						
	OUT_RESULT  :   out std_logic_vector ( WIDTH_FRACTION_RESULT downto 0 )             -- One integer bit + WIDTH_FRACTION_RESULT
) ; 
    
end entity tan_lut ;

architecture rtl_tan_lut of tan_lut is 

type type_lut_tan_real     is array ( 0 to 2 ** ( IN_ANGLE ' length ) - 1 ) of real ;
type type_lut_tan_slv      is array ( 0 to 2 ** ( IN_ANGLE ' length ) - 1 ) of std_logic_vector ( OUT_RESULT ' range ) ;

signal lut_tan_slv     		: type_lut_tan_slv ;
signal integer_part_of_in_angle	: unsigned ( 6 downto 0 ) ;

begin 

	integer_part_of_in_angle <=	unsigned ( IN_ANGLE ( IN_ANGLE ' high downto IN_ANGLE ' high - 6 ) ) ; 

    generate_tan_lut : for index in 0 to 2 ** ( IN_ANGLE ' length ) - 1
    generate 
                
        lut_tan_slv ( index )  <= std_logic_vector ( to_unsigned ( integer ( ( cos ( real ( index ) * ( MATH_PI / 180.0 ) / ( real ( 2 ** WIDTH_FRACTION_ANGLE ) ) ) ) * ( real ( 2 ** WIDTH_FRACTION_RESULT ) ) ) , OUT_RESULT ' length ) ) ;  
    
    end generate ;

    reading_from_memory : process ( IN_CLOCK ) is
    begin 
        if rising_edge ( IN_CLOCK ) then 
            OUT_RESULT <= lut_tan_slv ( to_integer ( unsigned ( IN_ANGLE ) ) ) ;
        end if ;
    end process reading_from_memory ;

end architecture rtl_tan_lut ;
 

Status
Not open for further replies.

Similar threads

Cookies are required to use this site. You must accept them to continue using the site. Learn more…