基于cordic ip核实现arctan

基于cordic ip核实现arctan@TOC

基于cordic ip核实现arctan

最近在研究相位补偿电路,需要用到反正切函数求解相位差,因此学习了该ip核的使用,写一篇小记录,希望能帮到有需要的盆友。

IP核介绍

简单粗暴地介绍一下IP核的使用。
基于cordic ip核实现arctan_第1张图片

  1. 首先将functional selection选为arc tan,随后设置输入位宽和输出位宽。
    需要注意的是,输入数据默认格式为“有2bit整数位的有符号定点小数”,输出数据默认格式为“有3bit整数位的有符号定点小数”,依据需要制定位宽即可。
    基于cordic ip核实现arctan_第2张图片

  2. 图中设置的input width为16bit,即cos和sin值都用16bit来表示,因此可以看到数据端口的输入数据总位宽为32bit。在实际写入数据时,将sin值写在[31:16]处,将cos值写在[15:0]处。
    需要注意的是,输入数据的总位宽一定为8的倍数。例如如果input width都为10bit,则有效数据为20bit,实际数据端口位宽为32bit。在实际写入数据时,需要将sin值写在[25:16]处,将cos值写在[9:0]处,[15:10]和[31:26]处补0即可。
    在这里插入图片描述

  3. 一通设置完以后,可以在左边栏看到延迟的时钟周期,如图所示延迟了20个时钟。
    基于cordic ip核实现arctan_第3张图片

仿真实现

为了验证该ip核的功能,写了一个.v文件,依次写入30°、45°、60°、60°、45°、30°、-30°、-45°、-60°、-60°、-45°、-30°… 的cos值和sin值,求取反正切的结果。
代码如下,较为基础,不做解释。

// A code block
module test_arctan(
input                   clk,
input                   rst_n,
output reg [15:0]      arctan_out_tdata,
output wire            arctan_out_tvalid
    );
 
 reg [2:0]                   state;
 reg [2:0]                   next_state;
 reg [4:0]                   cnt;
 reg [15:0]                  cos,sin;
 reg [31:0]                  arctan_in_tdata;
 reg                         arctan_in_tvalid; 
 wire[15:0]                  arctan_dout;
 
 localparam                  S_IDLE      =3'b001;
 localparam                  S_START     =3'b101;
 localparam                  S_CPT       =3'b100;
 localparam                  S_STOP      =3'b110;
 
 always@(posedge clk or negedge rst_n)
 begin
     if(rst_n==1'b0)
         state<=S_IDLE;
     else
         state<=next_state;
 end
 
 always@(*)
 begin
     case(state)
         S_IDLE:
             next_state<=S_START;
         S_START:
             next_state<=S_CPT;
         S_CPT:
             if(arctan_out_tvalid)
                 next_state<=S_STOP;
             else
                 next_state<=S_CPT;
         S_STOP:
             if(cnt<5'd23)
                 next_state<=S_START;
             else
                 next_state<=S_STOP;
        default:
            next_state<=state;
     endcase
 end
 
 always@(posedge clk or negedge rst_n)
 begin
     if(rst_n==1'b0)
         cnt<=0;
     else if(state==S_STOP && next_state!=state)
               cnt<=cnt+1;
           else
               cnt<=cnt;
 end
 
 always@(posedge clk or negedge rst_n)
 begin
     if(rst_n==1'b0)
         arctan_in_tvalid<=0;
     else if(state==S_START)
               arctan_in_tvalid<=1;
           else
               arctan_in_tvalid<=0;
 end
 
 always@(posedge clk or negedge rst_n)
 begin
     if(rst_n==1'b0)
         arctan_in_tdata<=0;
     else if(state==S_START)
               arctan_in_tdata<={sin,cos};
 end

 always@(posedge clk or negedge rst_n)
 begin
     if(rst_n==1'b0)
         arctan_out_tdata<=0;
     else if(next_state==S_STOP)
          begin
              if(cos[15]==1'b0)
                  arctan_out_tdata<=arctan_dout;
              else
                  arctan_out_tdata<=arctan_dout-16'b0110_0100_1000_0111;
          end
     else
         arctan_out_tdata<=arctan_out_tdata;
 end

 always@(*)
 begin
     case(cnt)
             5'd0: begin //30°
                       cos<=16'b0011_0111_0110_1100;//根号3/2
                       sin<=16'b0010_0000_0000_0000;//1/2                    
                   end    
             5'd1: begin //45°
                       cos<=16'b0010_1101_0100_0001;
                       sin<=16'b0010_1101_0100_0001;                   
                   end  
             5'd2: begin //60°
                       cos<=16'b0010_0000_0000_0000;
                       sin<=16'b0011_0111_0110_1100;                  
                   end 
             5'd3: begin //60°
                       cos<=16'b0010_0000_0000_0000;
                       sin<=16'b0011_0111_0110_1100;                  
                   end  
             5'd4: begin //45°
                       cos<=16'b0010_1101_0100_0001;
                       sin<=16'b0010_1101_0100_0001;                   
                   end                                                         
             5'd5: begin //30°
                       cos<=16'b0011_0111_0110_1100;//根号3/2
                       sin<=16'b0010_0000_0000_0000;//1/2                    
                   end    
             5'd6: begin //-30°
                       cos<=16'b1100_1000_1001_0100;
                       sin<=16'b0010_0000_0000_0000;                    
                   end 
             5'd7: begin //-45°
                       cos<=16'b1101_0010_1011_1111;
                       sin<=16'b0010_1101_0100_0001;                    
                   end 
             5'd8: begin //-60°
                       cos<=16'b1110_0000_0000_0000;
                       sin<=16'b0011_0111_0110_1100;                    
                   end                                                                           
             5'd9: begin //-60°
                       cos<=16'b1110_0000_0000_0000;
                       sin<=16'b0011_0111_0110_1100;                    
                   end                        
             5'd10: begin //-45°
                       cos<=16'b1101_0010_1011_1111;
                       sin<=16'b0010_1101_0100_0001;                    
                    end                  
             5'd11: begin //-30°
                       cos<=16'b1100_1000_1001_0100;
                       sin<=16'b0010_0000_0000_0000;                    
                    end           
             5'd12: begin //30°
                      cos<=16'b0011_0111_0110_1100;//根号3/2
                      sin<=16'b0010_0000_0000_0000;//1/2                    
                  end    
            5'd13: begin //45°
                      cos<=16'b0010_1101_0100_0001;
                      sin<=16'b0010_1101_0100_0001;                   
                  end  
            5'd14: begin //60°
                      cos<=16'b0010_0000_0000_0000;
                      sin<=16'b0011_0111_0110_1100;                  
                  end 
            5'd15: begin //60°
                      cos<=16'b0010_0000_0000_0000;
                      sin<=16'b0011_0111_0110_1100;                  
                  end  
            5'd16: begin //45°
                      cos<=16'b0010_1101_0100_0001;
                      sin<=16'b0010_1101_0100_0001;                   
                  end                                                         
            5'd17: begin //30°
                      cos<=16'b0011_0111_0110_1100;//根号3/2
                      sin<=16'b0010_0000_0000_0000;//1/2                    
                  end    
            5'd18: begin //-30°
                      cos<=16'b1100_1000_1001_0100;
                      sin<=16'b0010_0000_0000_0000;                    
                  end 
            5'd19: begin //-45°
                      cos<=16'b1101_0010_1011_1111;
                      sin<=16'b0010_1101_0100_0001;                    
                  end 
            5'd20: begin //-60°
                      cos<=16'b1110_0000_0000_0000;
                      sin<=16'b0011_0111_0110_1100;                    
                  end                                                                           
            5'd21: begin //-60°
                      cos<=16'b1110_0000_0000_0000;
                      sin<=16'b0011_0111_0110_1100;                    
                  end                        
            5'd22: begin //-45°
                      cos<=16'b1101_0010_1011_1111;
                      sin<=16'b0010_1101_0100_0001;                    
                   end                  
            5'd23: begin //-30°
                      cos<=16'b1100_1000_1001_0100;
                      sin<=16'b0010_0000_0000_0000;                    
                   end                                                                                                                                                 
             default: 
                   begin
                       cos<=16'b0000_0000_0000_0000;
                       sin<=16'b0000_0000_0000_0000;
                   end
     endcase
 end

 cordic_1 arctan (
      .aclk(clk),                                        // input wire aclk
      .s_axis_cartesian_tvalid(arctan_in_tvalid),  // input wire s_axis_cartesian_tvalid
      .s_axis_cartesian_tdata(arctan_in_tdata),    // input wire [31 : 0] s_axis_cartesian_tdata
      .m_axis_dout_tvalid(arctan_out_tvalid),            // output wire m_axis_dout_tvalid
      .m_axis_dout_tdata(arctan_dout)              // output wire [31 : 0] m_axis_dout_tdata
    );   
    
endmodule

问题分析

调试过程中有遇到一个问题。

  1. 当输入的正弦值为正,余弦值为正时,反正切得到的结果是对的,例如输入sin30°,cos30°时,输出结果为pi/6。
  2. 当输入的正弦值为负,余弦值为正时,反正切得到的结果也是对的,例如输入-sin30°,cos30°时,输出结果为-pi/6
  3. 但是当输入的正弦值为正,余弦值为负时,反正切得到的结果为正值,例如输入sin30°,-cos30°时,输出结果为6pi/5,不符号用户手册中对输出结果为-pi~pi的描述。

为了使第三种情况输出结果也为-pi/6,查了很多资料,最后在xilinx社区找到了解决办法。
基于cordic ip核实现arctan_第4张图片
因此,在代码中加了一段对cos值符号位的判断,由此来决定是否对输出结果进行“减去2pi”的操作。

 always@(posedge clk or negedge rst_n)
 begin
     if(rst_n==1'b0)
         arctan_out_tdata<=0;
     else if(next_state==S_STOP)
          begin
              if(cos[15]==1'b0)
                  arctan_out_tdata<=arctan_dout;
              else
                  arctan_out_tdata<=arctan_dout-16'b0110_0100_1000_0111;
          end
     else
         arctan_out_tdata<=arctan_out_tdata;
 end

最终输出正常,仿真结果如下。(图中为输入输出均为32bit的情况)
基于cordic ip核实现arctan_第5张图片

参考链接

xilinx社区的完整问题描述和解决方案: link.

你可能感兴趣的:(小欧的FPGA进阶日记,verilog,fpga)