Programmable Devices
CPLDs, FPGAs, SoC FPGAs, Configuration, and Transceivers
20706 Discussions

How can i use CORDIC ?

Altera_Forum
Honored Contributor II
1,986 Views

Hello i am trying to use the cordic core { http://opencores.org/project,cordic

with no sucess. 

Say i have a complex number of 4 + 5i. 

I have inserted 1000 as the Xi input and 1001 as the Yi input 

But the r2p_corproc is returning me wrong R and A values. It should be  

r = 6.4031 angle = 0.8961 {matlab} 

but instead i get 

r= 103 

a= 146811 

 

My test bench is : 

 

SIGNAL real_i : signed(15 downto 0) := "00000000"& "00000100" ; SIGNAL imag_i : signed(15 downto 0) := "00000000"& "00000101" ; SIGNAL ena_r_i : std_logic := '1'; SIGNAL rst_negado : std_logic := '1'; SIGNAL result_ang_o : unsigned(19 downto 0); SIGNAL result_mag_o : signed(19 downto 0) ; SIGNAL test_clk: std_logic := '0'; -- component instatiation component rl131 is port( clk_i : in std_logic; rst_i_n : in std_logic; ena_i : in std_logic; Real_i : in signed(15 downto 0); Imag_i : in signed(15 downto 0); Rout_o : out unsigned(19 downto 0); Aout_o : out signed(19 downto 0) ); end component; BEGIN dut : rl131 port map( clk_i => test_clk, ena_i => ena_r_i, rst_i_n => rst_negado, Real_i => real_i, Imag_i => imag_i, Rout_o => result_ang_o, Aout_o => result_mag_o); test_clk <= not test_clk after clkperiod/2;
0 Kudos
15 Replies
Altera_Forum
Honored Contributor II
951 Views

the outputs are 20 bits, 16 bits integer and 4 bits fraction so: 

 

magnitude of 103 (67 in hex) means 6.7 

 

and Angle of 146811 (23d7b) i.e 23d7.b degrees = 9175 and this wraps 25 times to 175 degrees. 

 

your figure of .8961 is in rads I believe so becomes .8961 * 180/pi = 51 degrees. 

So at least magnitude seems working. 

 

The angle may not actually have been meant to be wrapped up, it then does not need 16 bits for integer part (range is 0 to 360) i.e. only 9 bits may represent it and the rest 11 bits as fraction hence A = 146811/2^11 = 72 degrees which I think is a bit better than 175 !!! 

 

The documentation is poor or the code does not work I am afraid.
0 Kudos
Altera_Forum
Honored Contributor II
951 Views

Hi kas thanks for the help but it is 6.4375 and not 6.7 :P

0 Kudos
Altera_Forum
Honored Contributor II
951 Views

>> magnitude of 103 (67 in hex) means 6.7 

 

Additional info: 6.7 in fixed decimal (16.4) representation is the real value of 6.4375. In binary representation 103 = 1100111 or 0000000000000110.0111 in fixed decimal (16.4). 

 

Taking the last 4 digits as the fractional part gives: 

0 * (0.5) + 1 * (0.25) + 1 * (0.125) + 1 * (0.0625) = .4375, so you have a fixed point representation of 6.4375 which is slightly over matlab's result of 6.4031. A 6.6 fixed decimal (16.4) value is a real value of 6.375 which is slightly under, but closer to the actual result from matlab. 

 

I think the cordic routine isn't expected to get the last fractional digit perfect, so it appears that may be working for your r value. 

 

I haven't tried to analyze the angle portion, nor do I know how at this point in time.
0 Kudos
Altera_Forum
Honored Contributor II
951 Views

lol, aparado. you posted while I was trying to figure out the angle portion....I lied....I tried to analyze it. :)

0 Kudos
Altera_Forum
Honored Contributor II
951 Views

Thanks nails. true, 7 out of 16 is 7/16 = .4375 

 

The angle seems wrong completely
0 Kudos
Altera_Forum
Honored Contributor II
951 Views

A second thought about angle. It could be that representation is such that 2^16 = 360 degrees hence in your case 9175 => 360 * 9175/2^16 

= 50.4 degrees , very close to Matlab of 51.3 degrees
0 Kudos
Altera_Forum
Honored Contributor II
951 Views

Ok now i am trying to do my own CORDIC core to have the magnitude and phase value or a real/imaginary phasor. 

I am following this algorithm: 

http://www.dspguru.com/dsp/faqs/cordic 

and trying to make a RTL out of it. 

 

My matlab code is working and is the following 

function v = cordic(real, imaginaria,n) % This function computes v = (beta in radians) % using n iterations. Increasing n will increase the precision. multiplier = 2^22; tmp_real = 0; if (real < 0) tmp_real = real; if imaginaria > 0 real = imaginaria; imaginaria = -tmp_real; acc_phase_rads = -(pi/2)* multiplier ; else real = -imaginaria; imaginaria = tmp_real; acc_phase_rads = pi/2 * multiplier; end; else acc_phase_rads = 0; end; % Initialization of tables of constants used by CORDIC % need a table of arctangents of negative powers of two, in radians: % angles = atan(2.^-(0:27)); % and a table of products of reciprocal lengths of vectors : ka = 1; for j=1:n; Kvalues(j) = ka angles(j) = atan(ka) ka = ka * 0.5; end; for l=1:n; K = Kvalues(l) * multiplier; phase_rads = angles(l) * multiplier; tmp_real = real; if (imaginaria >=0) real = (real*multiplier + imaginaria * K)/multiplier; imaginaria = (multiplier*imaginaria - tmp_real * K)/multiplier; acc_phase_rads = (acc_phase_rads - phase_rads ); else real = (real*multiplier - imaginaria * K)/multiplier; imaginaria = (imaginaria*multiplier + tmp_real * K)/multiplier; acc_phase_rads = (acc_phase_rads + phase_rads); end; end; result_phase_rads = -acc_phase_rads/multiplier result_mag = real * (1/1.6468) return 

 

My VHDL code at the moment is the following 

library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.std_logic_signed.all; -- entity declaration entity cordic_V1 is port( -- system signals clk_i : in std_logic; rst_i_n : in std_logic; -- interface signals ena_i : in std_logic; real_i : in std_logic_vector(31 downto 0); imag_i : in std_logic_vector(31 downto 0); fase_o : out std_logic_vector(31 downto 0); mag_o : out std_logic_vector(31 downto 0); finish_cordic : out std_logic ); end cordic_V1; begin angulo_inst : angulo_atan port map( clk_i => clk_i, rst_i_n => rst_i_n, indice_i => std_logic_vector(indice), angulo_o => angulo_indice); ganho_inst : ganho_k port map( clk_i => clk_i, rst_i_n => rst_i_n, indice_i => std_logic_vector(indice_2), ganho_o => ganho_indice); fase_o <= std_logic_vector (fase_o_reg(31 downto 0)); mag_o <= std_logic_vector (mag_o_reg(31 downto 0)); finish_cordic <= finish_cordic_reg; -- State machine process process (rst_i_n, clk_i) is begin if (rst_i_n = '0') then ... elsif rising_edge(clk_i) then state_core <= state_core_next; indice <= indice_next; indice_2 <= indice_2_next; parte_real <= parte_real_next; parte_imaginaria <= parte_imaginaria_next; temp_parte_real <= temp_parte_real_next; fase_o_reg <= fase_o_reg_next; mag_o_reg <= mag_o_reg_next; phase_rads <= phase_rads_next; acc_phase_rads <= acc_phase_rads_next; K_reg <= K_reg_next; tmp_mult_im_k <= tmp_mult_im_k_next; tmp_mult_re_k <= tmp_mult_re_k_next; tmp_operacoes_reg <= tmp_operacoes_reg_next; tmp_operacoes_acc_reg <= tmp_operacoes_acc_reg_next; finish_cordic_reg <= finish_cordic_reg_next; end if; end process; process (state_core, indice, indice_2, parte_real, parte_imaginaria, temp_parte_real, fase_o_reg, mag_o_reg, phase_rads, acc_phase_rads, K_reg, tmp_mult_im_k, tmp_mult_re_k, tmp_operacoes_reg, tmp_operacoes_acc_reg, real_i, imag_i, ganho_indice, angulo_indice, finish_cordic_reg) begin state_core_next <= state_core; indice_next <= indice; indice_2_next <= indice_2; p... case state_core is when INIT => -- initialize all the registers indice_next <= (others => '0'); i... state_core_next <= CORRIGE_Q_P1; when CORRIGE_Q_P1 => parte_real_next <= real_i & ZERO_STUFF; parte_imaginaria_next <= imag_i & ZERO_STUFF; state_core_next <= CORRIGE_Q_P2; when CORRIGE_Q_P2 => if parte_real(52) = '1' then temp_parte_real_next <= parte_real; state_core_next <= CORRIGE_Q_P3; else acc_phase_rads_next <= (others => '0'); state_core_next <= CALCULA_CORDIC_P1; end if; when CORRIGE_Q_P3 => if parte_imaginaria(52) = '0' then parte_imaginaria_next <= not (temp_parte_real) ;--+ 1; else parte_real_next <= not (parte_imaginaria);-- + 1; end if; state_core_next <= CORRIGE_Q_P4; when CORRIGE_Q_P4 => if parte_imaginaria(52) = '0' then parte_real_next <= parte_imaginaria; parte_imaginaria_next <= parte_imaginaria + 1; acc_phase_rads_next <= "111111111" & pi_2 & ZERO_STUFF; -- -pi_2 else parte_real_next <= parte_real + 1;-- + 1; parte_imaginaria_next <= temp_parte_real; acc_phase_rads_next <= "000000000" & pi_2 & ZERO_STUFF; end if; state_core_next <= CALCULA_CORDIC_P1; when CALCULA_CORDIC_P1 => K_reg_next <= "000000000" & ZERO_STUFF & ganho_indice; phase_rads_next <= "000000000" & ZERO_STUFF & angulo_indice; temp_parte_real_next <= parte_real; state_core_next <= CALCULA_CORDIC_P2; --tmp_operacoes_reg acc real --tmp_operacoes_acc_reg acc imaginario when CALCULA_CORDIC_P2 => tmp_mult_im_k_next <= parte_imaginaria * K_reg; tmp_mult_re_k_next <= temp_parte_real * K_reg; state_core_next <= CALCULA_CORDIC_P2_1; when CALCULA_CORDIC_P2_1 => if tmp_mult_im_k(105) = '0' then tmp_mult_im_k_next <= ZERO_STUFF & tmp_mult_im_k(105 downto ZERO_STUFF'length); tmp_mult_re_k_next <= ZERO_STUFF & tmp_mult_re_k(105 downto ZERO_STUFF'length) ; else tmp_mult_im_k_next <= ONE_STUFF & tmp_mult_im_k(105 downto ZERO_STUFF'length); tmp_mult_re_k_next <= ONE_STUFF & tmp_mult_re_k(105 downto ZERO_STUFF'length) ; end if; state_core_next <= CALCULA_CORDIC_P3; when CALCULA_CORDIC_P3 => if parte_imaginaria(52) = '0' then tmp_operacoes_reg_next <= parte_real + tmp_mult_im_k; tmp_operacoes_acc_reg_next <= parte_imaginaria - tmp_mult_re_k; acc_phase_rads_next <= acc_phase_rads - phase_rads; else tmp_operacoes_reg_next <= parte_real - tmp_mult_im_k; tmp_operacoes_acc_reg_next <= parte_imaginaria + tmp_mult_re_k; acc_phase_rads_next <= acc_phase_rads + phase_rads; end if; state_core_next <= CALCULA_CORDIC_P4; when CALCULA_CORDIC_P4 => if tmp_operacoes_reg(52) = '0' then parte_real_next <= ZERO_STUFF & tmp_operacoes_reg(52 downto ZERO_STUFF'length); else parte_real_next <= ONE_STUFF & tmp_operacoes_reg(52 downto ZERO_STUFF'length); end if; if tmp_operacoes_acc_reg(52) = '0' then parte_imaginaria_next <= ZERO_STUFF & tmp_operacoes_acc_reg(52 downto ZERO_STUFF'length); else parte_imaginaria_next <= ONE_STUFF & tmp_operacoes_acc_reg(52 downto ZERO_STUFF'length); end if; state_core_next <= CALCULA_CORDIC_P5; when CALCULA_CORDIC_P5 => if indice < 24 then indice_next <= indice + 1; indice_2_next <= indice_2 + 1; state_core_next <= CALCULA_CORDIC_P6; elsif indice = 24 then state_core_next <= FIM_P1; end if; when CALCULA_CORDIC_P6 => state_core_next <= CALCULA_CORDIC_P1; when FIM_P1 => fase_o_reg_next <= not (ZERO_STUFF & acc_phase_rads(52 downto 21)); -- mag_o_reg_next <= parte_real; -- finish_cordic <= '1'; state_core_next <= FIM_P2; when FIM_P2 => fase_o_reg_next <= fase_o_reg + 1; mag_o_reg_next <= parte_real; finish_cordic_reg_next <= '1'; state_core_next <= INIT; when others => state_core_next <= INIT; end case; end process; end rtl_cordic;  

 

any help is really welcome, i am having problem with negative angles in the middle of my calculos...
0 Kudos
Altera_Forum
Honored Contributor II
951 Views

What kind of problems exactly? And just a remark: 

--- Quote Start ---  

use ieee.numeric_std.all; 

use ieee.std_logic_signed.all; 

--- Quote End ---  

This is a bad idea, mixing numeric_std with one of the non standard std_logic_(un)signed gives weird results and problems. You'd better only include the numeric_std library and use the unsigned/signed types each time you need to do an arithmetic operation.
0 Kudos
Altera_Forum
Honored Contributor II
951 Views

 

--- Quote Start ---  

What kind of problems exactly? And just a remark:This is a bad idea, mixing numeric_std with one of the non standard std_logic_(un)signed gives weird results and problems. You'd better only include the numeric_std library and use the unsigned/signed types each time you need to do an arithmetic operation. 

--- Quote End ---  

 

 

Actually it doesnt, its std_logic_arith and numeric_std that clash. 

 

but you shouldnt be using std_logic_signed because it allows you to treat std_logic_vectors as signed integers, when the signed type is far more appropriate.
0 Kudos
Altera_Forum
Honored Contributor II
951 Views

What's the difference?

0 Kudos
Altera_Forum
Honored Contributor II
951 Views

Also i am trying to represent these numbers in binary: 

-0.5 

-0.86 

How can i represent them? I am using fixed point and shifting my number 2^22 times;;
0 Kudos
Altera_Forum
Honored Contributor II
951 Views

The main problem with using arithmetic on std_logic_vectors, is that you don't know at first glance when reading the code if you are dealing with signed or unsigned numbers. Sure for know you know what you wrote, but your code could end up copy/pasted in another file, where std_logic_vectors are supposed to be unsigned instead, or you could even have to deal with unsigned and signed values in the same code. Using the unsigned and signed types wakes the code more readable, more reusable and can reduce mistakes. 

As for the binary numbers, even if real arithmetic operations aren't synthesizable, you can still use real constants in your code. Something like: 

--- Quote Start ---  

constant MyNum : integer := integer(0.85*(2**22)); 

--- Quote End ---  

Or if using vectors and declaring more constants, something like (assuming you are using 23 bit signed numbers, to have a scaling factor of 2^22): 

--- Quote Start ---  

constant VectorsSize : natural := 23; 

constant ScalingFactor : real := real(2**(VectorsSize-1)); 

constant MyVector : signed := to_signed(integer(0.85*ScalingFactor),VectorsSize); 

--- Quote End ---  

 

If you need to do a lot of conversions, you could also define a function to do that instead. 

There is also a new fixed point library in VHDL, but I haven't tried it yet.
0 Kudos
Altera_Forum
Honored Contributor II
951 Views

Thanks Daix. Now i understand. 

However i can't multipliy a std_logic_vector (or a signed) for an integer. Can i? 

And does this := to_signed(integer(0.85*ScalingFactor),VectorsSize) ; work? Is it sinthetizable? I think i saw this on the fixed_point lib 

 

i tried the following: 

 

constant um_terco : signed (40 downto 0) := to_signed(integer(0.3333),41); signal Va1_real : std_logic_vector (117 downto 0); signal temp_fasor_real8 : std_logic_vector (76 downto 0); Va1_real = std_logic_vector(um_terco * signed(temp_fasor_real8))  

where temp-fasor_real8 = 1773 in binary 

however the result was 0..
0 Kudos
Altera_Forum
Honored Contributor II
951 Views

You can multiply a signed/unsigned with an integer with the numeric_std library. Be careful with multiplying fixed point vectors though, as you will need to shift the result back. 

 

As for your 0 result, it is because with this assignment: 

 

--- Quote Start ---  

constant um_terco : signed (40 downto 0) := to_signed(integer(0.3333),41); 

--- Quote End ---  

 

0.3333 is converted to the integer 0. You forgot to multiply 0.3333 by the scaling factor.
0 Kudos
Altera_Forum
Honored Contributor II
951 Views

with the fixed point library, you dont need to worry about scaling factors: 

 

constant UM_TERCO : sfixed := to_sfixed(0.3333, N-1, -M); --N integer bits, M fractional signal temp_fasor_real8 : sfixed(A downto 0) signal Va_real : signed(N+A-1 downto -M); Va_real <= temp_fasor_real8 * UM_TERCO;
0 Kudos
Reply