基於FPGA的cordic算法的verilog初步實現


  最近在看cordic算法,由於還不會使用matlab,真是痛苦,一系列的筆算才大概明白了這個算法是怎么回事。於是嘗試用verilog來實現。用verilog實現之前先參考軟件的程序,於是先看了此博文http://blog.csdn.net/liyuanbhu/article/details/8458769 也不截圖了,因為怕圖形被其他博客網站檢測到后屏蔽圖片,造成此博文無法正常閱讀。

閱讀此博文,需要先閱讀上面這個博文的內容。

  這是此博文中的C代碼。避免浮點運算,所以angle數組里面的角度值都擴大了256倍。此程序中計算點(10,20)與X軸之間的夾角。最終運行結果是16238。而16238/256=63.43

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 //double my_atan2(double x, double y);
 5 
 6 
 7 double  my_atan2  (int x, int y)
 8 {
 9     const int angle[] = {11520, 6801, 3593, 1824, 916, 458, 229, 115, 57, 29, 14, 7, 4, 2, 1};
10 
11     int i = 0;
12     int x_new, y_new;
13     int angleSum = 0;
14 
15     x *= 1024;// ½« X Y ·Å´óһЩ£¬½á¹û»á¸ü׼ȷ
16     y *= 1024;
17 
18     printf ("org_x = %d, org_y=%d\n",x,y);
19 
20     for(i = 0; i < 15; i++)
21     {
22         if(y > 0)
23         {
24             x_new = x + (y >> i);
25             y_new = y - (x >> i);
26             x = x_new;
27             y = y_new;
28             angleSum += angle[i];
29         }
30         else
31         {
32             x_new = x - (y >> i);
33             y_new = y + (x >> i);
34             x = x_new;
35             y = y_new;
36             angleSum -= angle[i];
37         }
38         printf("Debug: i = %d x=%d, y =%d angleSum = %d, angle = %d\n", i,x,y ,angleSum, angle[i]);
39     }
40     return angleSum;
41 }
42 
43 void main()
44 {
45     double z=0 ; 
46     z = my_atan2(10.0, 20.0);
47     printf("\n z = %lf \n", z);
48 
49 }

  既然有了C 就很好進行verilog設計。

  先談架構。

  1,先將角度數據放到一個rom中進行存儲

  2,取一個數,運算一個數。直到循環結束。

  這也就是所謂的cordic算法的向量模式。用來求角度。先看頂層

 1 //
 2 //
 3 //
 4 //
 5 
 6 module cordic_rotation_top (
 7                             clock ,
 8                             rst_n ,
 9                             x_crd,
10                             y_crd,
11                             ena ,
12                             deg_sum
13                             ); 
14 input         clock ; 
15 input         rst_n ; 
16 input     [15:0]    x_crd ; 
17 input     [15:0]    y_crd ; 
18 input             ena ; 
19 output     [15:0]     deg_sum ; 
20 
21 
22 wire     [4:0] deg_addr ; 
23 wire     [15:0] deg_data ; 
24 
25 alt_ip_rom_cordic u_rom (
26                             .address  (deg_addr),
27                             .clock    (clock),
28                             .q        (deg_data)
29                         );
30 
31 
32 
33 cordic_rotation  u_cord (
34                         .clk      (clock),
35                         .rst_n    (rst_n),
36                         .ena      (ena),
37                         .x_crd    (x_crd),
38                         .y_crd    (y_crd),
39                         .deg_data (deg_data),
40                         .deg_addr (deg_addr),
41                         .deg_sum  (deg_sum)
42                         );
43                         
44 endmodule 

rom的初始化文件為

再看運算單元。

 1 module cordic_rotation (
 2                         clk ,
 3                         rst_n ,
 4                         ena ,
 5                         x_crd,
 6                         y_crd,
 7                         deg_data,
 8                         deg_addr,
 9                         deg_sum
10                         );
11                         
12 input     clk ; 
13 input     rst_n ; 
14 input     ena ; 
15 input     [15:0]    x_crd ; //x coordinate
16 input     [15:0]    y_crd ; //y coordinate
17 input     [15:0]     deg_data ; 
18 output     [4:0]     deg_addr ; 
19 output     reg[15:0]     deg_sum ; 
20 
21 // ------ rotation count  0 - 14   -------
22 reg     [4:0] rot_cnt ; 
23 reg     [4:0] rot_cnt_r ; 
24 wire             opr_en ; 
25 assign opr_en = ((rot_cnt_r<5'd15)&(ena)) ; 
26 
27 always @ (posedge clk or negedge rst_n)
28     if (!rst_n) begin 
29             rot_cnt <= 5'd0 ; 
30             rot_cnt_r<= 5'd0; 
31             end 
32     else if (opr_en) begin 
33             rot_cnt    <= rot_cnt + 5'd1 ; 
34             rot_cnt_r<= rot_cnt ; 
35             end 
36     else if (!ena) begin 
37             rot_cnt <= 5'd0 ; 
38             rot_cnt_r<= rot_cnt ; 
39             end 
40 assign deg_addr = rot_cnt ; 
41 
42 //---------------------------------------
43 reg                 cal_cnt ; 
44 reg signed [16:0]     x_d,    y_d ; 
45 always @ (posedge clk or negedge rst_n)
46     if (!rst_n) begin 
47             x_d     <= 17'd0 ; 
48             y_d     <= 17'd0 ; 
49             deg_sum <= 16'd0 ; 
50             cal_cnt <= 1'd0 ; 
51             end 
52     else if (opr_en) begin 
53             case (cal_cnt)
54                 1'd0 : begin 
55                         x_d     <= {1'd0,x_crd}; 
56                         y_d     <= {1'd0,y_crd};
57                         deg_sum <= 16'd0 ; 
58                         cal_cnt <= 1'd1 ; 
59                         end 
60                 1'd1 : begin 
61                         if ((y_d[16])|(y_d==16'd0)) begin 
62                                 x_d     <= x_d - (y_d >>> rot_cnt_r); 
63                                 y_d     <= y_d + (x_d >>> rot_cnt_r) ;
64                                 deg_sum <= deg_sum - deg_data ; 
65                                 end 
66                         else begin 
67                                 x_d     <= x_d + (y_d >>> rot_cnt_r); 
68                                 y_d     <= y_d - (x_d >>> rot_cnt_r) ;
69                                 deg_sum <= deg_sum + deg_data ; 
70                                 end 
71                         end 
72             endcase 
73             end 
74     else begin 
75             cal_cnt <= 1'd0 ; 
76             end
77     
78 endmodule 

rot_cnt作為rom的地址。但是由於rom返回度數值需要一個周期,所以下面的運算單元需要等待一個周期才可以進行運算,於是有了rot_cnt_r這個參數。

 

於是問題又來了,上面的是向量模式。還有一個旋轉模式。於是開始搗鼓。

稍微將上述的C代碼進行修改,計算30度的cos30,以及sin30 

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 //double my_atan2(double x, double y);
 5 
 6 
 7 double  my_atan2  (int x, int y)
 8 {
 9     const int angle[] = {11520, 6801, 3593, 1824, 916, 458, 229, 115, 57, 29, 14, 7, 4, 2, 1};
10 
11     int i = 0;
12     int x_new, y_new;
13     int angleSum = 30*256;
14 
15     x *= 1024;// ½« X Y ·Å´óһЩ£¬½á¹û»á¸ü׼ȷ
16     y *= 1024;
17 
18     printf ("org_x = %d, org_y=%d\n",x,y);
19 
20     for(i = 0; i < 15; i++)
21     {
22         if(angleSum <= 0)
23         {
24             x_new = x + (y >> i);
25             y_new = y - (x >> i);
26             x = x_new;
27             y = y_new;
28             angleSum += angle[i];
29         }
30         else
31         {
32             x_new = x - (y >> i);
33             y_new = y + (x >> i);
34             x = x_new;
35             y = y_new;
36             angleSum -= angle[i];
37         }
38         printf("Debug: i = %d x=%d, y =%d angleSum = %d, angle = %d\n", i,x,y ,angleSum, angle[i]);
39     }
40     return angleSum;
41 }
42 
43 void main()
44 {
45     double z=0 ; 
46     z = my_atan2(1.0, 0.0);
47     printf("\n z = %lf \n", z);
48 
49 }

結果是這樣子的

先看已知條件:

1,cordic算法中的Kn極限值=1.6476 。  1/Kn=0.6073. 

2,上述軟件設置y=0. x=1.並且坐標值擴大了1024倍。從公式上,cosa= x      sina= y     角度a=30度

3,運算結果x=1461.  y=844 

好,開始運算  cosa=1461/(1024*1.6476)=0.8659。 cos30=0.8660.

      sina=844/(1024*1.6476) = 0.500    。 sin30 = 0.5 

精度由loop次數相關

 

好,既然驗證了這個軟件程序是可以的。那么就將它轉換成Verilog的程序

 1 //cordic 算法的旋轉模式 
 2 
 3 
 4 module cordic_rotation (
 5                         clk ,
 6                         rst_n ,
 7                         ena ,
 8                         x_crd,
 9                         y_crd,
10                         deg_data,
11                         deg_addr,
12                         deg_sum
13                         );
14                         
15 input     clk ; 
16 input     rst_n ; 
17 input     ena ; 
18 input     [15:0]    x_crd ; //x coordinate
19 input     [15:0]    y_crd ; //y coordinate
20 input     [15:0]     deg_data ; 
21 output     [4:0]     deg_addr ; 
22 output     [15:0]     deg_sum ; 
23 
24 
25 parameter   DEGREE = 30 ;
26 parameter     DEG_PARA =  (DEGREE*256) ; 
27 // ------ rotation count  0 - 14   -------
28 reg     [4:0] rot_cnt ; 
29 reg     [4:0] rot_cnt_r ; 
30 wire             opr_en ; 
31 assign opr_en = ((rot_cnt_r<5'd15)&(ena)) ; 
32 
33 always @ (posedge clk or negedge rst_n)
34     if (!rst_n) begin 
35             rot_cnt <= 5'd0 ; 
36             rot_cnt_r<= 5'd0; 
37             end 
38     else if (opr_en) begin 
39             rot_cnt    <= rot_cnt + 5'd1 ; 
40             rot_cnt_r<= rot_cnt ; 
41             end 
42     else if (!ena) begin 
43             rot_cnt <= 5'd0 ; 
44             rot_cnt_r<= rot_cnt ; 
45             end 
46 assign deg_addr = rot_cnt ; 
47 
48 //---------------------------------------
49 reg                 cal_cnt ; 
50 reg signed [15:0]     deg_sum_r  ; 
51 reg signed [16:0]     x_d,    y_d ; 
52 always @ (posedge clk or negedge rst_n)
53     if (!rst_n) begin 
54             x_d     <= 17'd0 ; 
55             y_d     <= 17'd0 ; 
56             deg_sum_r <= 16'd0 ; 
57             cal_cnt <= 1'd0 ; 
58             end 
59     else if (opr_en) begin 
60             case (cal_cnt)
61                 1'd0 : begin 
62                         x_d     <= {1'd0,x_crd}; 
63                         y_d     <= {1'd0,y_crd};
64                         deg_sum_r <= DEG_PARA ; 
65                         cal_cnt <= 1'd1 ; 
66                         end 
67                 1'd1 : begin 
68                         if ((deg_sum_r[15])|(deg_sum_r==16'd0)) begin  //<=0
69                                 x_d     <= x_d + (y_d >>> rot_cnt_r); 
70                                 y_d     <= y_d - (x_d >>> rot_cnt_r) ;
71                                 deg_sum_r <= deg_sum_r + deg_data ; 
72                                 end 
73                         else begin 
74                                 x_d     <= x_d - (y_d >>> rot_cnt_r); 
75                                 y_d     <= y_d + (x_d >>> rot_cnt_r) ;
76                                 deg_sum_r <= deg_sum_r - deg_data ; 
77                                 end 
78                         end 
79             endcase 
80             end 
81     else begin 
82             cal_cnt <= 1'd0 ; 
83             end
84             
85 assign deg_sum = deg_sum_r ; 
86     
87 endmodule 

這個程序也是在上一個程序的基礎上修改的。所以盡量少改動。而是修改了tb。所以需要看看TB是怎么弄的

 1 ///
 2 //
 3 //
 4 //
 5 `timescale 1ns/100ps
 6 
 7 
 8 module cordic_rotation_top_tb ; 
 9 reg clock ,rst_n ; 
10 reg [15:0]     x_crd ,y_crd ; 
11 reg ena ; 
12 
13 wire [15:0] deg_sum ; 
14 
15 
16 cordic_rotation_top u_top (
17                             .clock   (clock),
18                             .rst_n   (rst_n),
19                             .x_crd   (x_crd),
20                             .y_crd   (y_crd),
21                             .ena     (ena),
22                             .deg_sum (deg_sum)
23                             ); 
24                             
25 always #10 clock = ~clock ; 
26 initial begin 
27         clock = 0 ;  rst_n =0 ; 
28         x_crd = 1*1024 ; y_crd=0; 
29         ena = 1 ; 
30         #40 rst_n = 1 ; 
31         #350 
32 /*        
33         ena = 0 ; 
34         #40 
35         x_crd = 10*1024 ; y_crd=10*1024; 
36         ena = 1 ; 
37         #350 
38 
39         ena = 0 ; 
40         #40 
41         x_crd = 20*1024 ; y_crd=10*1024; 
42         ena = 1 ; 
43         #350  */
44         $stop ;
45         end 
46                             
47 endmodule 

將第28行替換為     x_crd = 10*1024 ; y_crd=20*1024;     就變成了上一個工程的測試代碼。

 

此程序輸出結果為:

和軟件程序輸出結果相同。

 

有沒有發現問題,在計算這些東西的時候,很多網絡博文說精度和loop次數相關。但是這個擴大256倍運算到第11次就已經定住了。后面的輸出結果白循環了。

 

 

歡迎加入: FPGA廣東交流群:162664354

      FPGA開發者聯盟: 485678884

    微信公眾號:FPGA攻城獅之家

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM