在fpga中用Cordic算法来产生正弦函数

 在fpga中实现正弦函数可有三种基本方法,Cordic法和查找表法和线性插值法,三种方法各有其优劣性,今天就使用Cordic算法来产生正弦函数

CORDIC ( Coordinate Rotation Digital Computer ) 是坐标旋转数字计算机算法的简称,由 Vloder• 于 1959 年在设计美国航空导航控制系统的过程中首先提出, 主要用于解决导航系统中三角函数、 反三角函数和开方等运算的实时计算问题。 1971 年, Walther 将圆周系统、 线性系统和双曲系统统一到一个 CORDIC 迭代方程里 , 从而提出了一种统一的CORDIC 算法形式
Cordic算法是一种化繁为简的算法,在运算时,仅需要进行移位和加法的迭代操作,Cordic有两种计算模式,分别为旋转模式和向量模式,可分别在三种坐标系中来进行使用,对应可衍生8种计算模式,然后这8种模式相结合可衍生更多算法
在这里插入图片描述
今天就介绍圆坐标系中的旋转模式来产生正弦余弦函数

在fpga中用Cordic算法来产生正弦函数_第1张图片

在圆坐标中,设初始点为P1,经旋转θ角后到达P2点,可求出P2点的坐标为
x1 = x0cosθ - y0sinθ
y1 = x0sinθ + y0cosθ

变化为
x1 = cosθ(x0 - y0tanθ)
y1 = cosθ(y0 + x0tanθ)

设P1旋转角度θ后到达Pm,则
xm = cosθ(x0 - y0tanθ)
ym = cosθ(y0 + x0tanθ)
将θ角分为n个固定角θi(tanθi = 2^-i),则旋转角逆时针旋转逐次累加直至逼近Pm。∑θi的取值范围在[-99.7 99.7]之间,在运算前,首先在旋转过程中要将θ进行三角转换到规定的区间。由于旋转角固定,所以在旋转过程中可能超过θ,所以定义转动方向di,若∑θi小于θ,则di为正,则继续逆时针旋转,若∑θi大于θ,则di为负,则顺时针旋转。
设定dz+i = dz - diθi,z0 = θ,z逐次逼近直至为0,误差较小。

x1 = cosθ0(x0 - d0y0tanθ0) x2= cosθ1(x1 - d1y1tan1) = cosθ1cosθ0(x0 + d0d1x0tanθ0tanθ1 - d0y0tanθ0 - d1y0tanθ1)
y1 = cosθ0(y0 + d0x0tanθ0), y2 = cosθ1(y1 + d1x1tan1) = cosθ0cosθ1(y0 - d0x0tanθ0 - d1x0tanθ1 + d0d1y0tanθ0tanθ1)
在迭代过程中发现式中每一级cosθi可以提取出来,而tanθi已知(tanθi = 2^-i),所以在运算过程中将cosθi提取出并去掉得到
x1 = x0 - d0y0tanθ0
y1 = y0 + d0x0tanθ0
在fpga中用Cordic算法来产生正弦函数_第2张图片
将这个方程式称为伪旋转,旋转的角度是正确的,但因为模值R= 根号下x^2 + y^2;将cosθ去掉之后的模值R = 根号下(x^2 + y^2)/cosθ 其值扩大了1/cosθ倍。 迭代的最终结果xm = 1/∏cosθi * xm,即 xm = 1/∏cosθi(x0cosθ - y0sinθ)ym = 1/∏cosθi * ym ym = 1/∏cosθi(x0sinθ + y0cosθ) 令 x0 = ∏cosθi,y0 = 0,得到xm = cosθ
`ym = sinθ

在fpga设计中采用16级流水线设计,每一级流水线需进行三次加法运算,两次移位操作和进行一次查找表
在fpga中用Cordic算法来产生正弦函数_第3张图片

这里进行16次迭代的角度和∏cosθi对应如下,在第16次时,cosθi的值以接近为一,说明∏cosθi的值已接近为定值,这时运算结果较为准确。
总共运算有三个方程式
xi+1 = xi - di2^iyi ( i = 0……15)
yi+1 = yi + di2^ixi ( i = 0……15)
zi+1 = zi - diθi ( i = 0……15)
下面为实现代码

module Cordic`_design(

	clk,
	Rst_n,
	Sinx,
	Cosx,
	phase,
	error
);


	input clk;
	input Rst_n;
	input [31:0]phase;//输入的角度
	output reg signed [31:0] Sinx;
	output reg signed [31:0] Cosx;
	output reg signed [31:0] error;
	
//角度值	
`define rot0  32'd2949120    	//45*2^16    
`define rot1  32'd1740992    	//26.565*2^16
`define rot2  32'd919872    	//14.036*2^16
`define rot3  32'd466944    	//7.125*2^16
`define rot4  32'd234368    	//3.576*2^16
`define rot5  32'd117312    	//1.790*2^16
`define rot6  32'd58688    	//0.895*2^16
`define rot7  32'd29312    	//0.448*2^16
`define rot8  32'd14656    	//0.224*2^16
`define rot9  32'd7360    		//0.112*2^16
`define rot10 32'd3648    		//0.056*2^16
`define rot11 32'd1856			//0.028*2^16
`define rot12 32'd896    		//0.014*2^16
`define rot13 32'd448    		//0.007*2^16
`define rot14 32'd256    		//0.003*2^16
`define rot15 32'd128    		//0.002*2^16

parameter Pipeline = 16;//迭代次数
parameter K = 32'h09b74;	//K=0.607253*2^16,32'h09b74,
	
	reg signed 	[31:0] 		x0=0,y0=0,z0=0;
	reg signed 	[31:0] 		x1=0,y1=0,z1=0;
	reg signed 	[31:0] 		x2=0,y2=0,z2=0;
	reg signed 	[31:0] 		x3=0,y3=0,z3=0;
	reg signed 	[31:0] 		x4=0,y4=0,z4=0;
	reg signed 	[31:0] 		x5=0,y5=0,z5=0;
	reg signed 	[31:0] 		x6=0,y6=0,z6=0;
	reg signed 	[31:0] 		x7=0,y7=0,z7=0;
	reg signed 	[31:0] 		x8=0,y8=0,z8=0;
	reg signed 	[31:0] 		x9=0,y9=0,z9=0;
	reg signed 	[31:0] 		x10=0,y10=0,z10=0;
	reg signed 	[31:0] 		x11=0,y11=0,z11=0;
	reg signed 	[31:0] 		x12=0,y12=0,z12=0;
	reg signed 	[31:0] 		x13=0,y13=0,z13=0;
	reg signed 	[31:0] 		x14=0,y14=0,z14=0;
	reg signed 	[31:0] 		x15=0,y15=0,z15=0;
	reg signed 	[31:0] 		x16=0,y16=0,z16=0;
		
	reg [1:0]Quadrant[Pipeline:0];//确定角的象限
	
	always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x0 <= 0; 						
		y0 <= 0;
		z0 <= 0;
	end
	else
	begin
		x0 <= K;
		y0 <= 32'd0;
		z0 <= (phase[15:0] << 16);//phase[15:0]为角度值
	end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x1 <= 0; 						
		y1 <= 0;
		z1 <= 0;
	end
	else if(z0[31])
	begin
      x1 <= x0 + y0;
      y1 <= y0 - x0;
      z1 <= z0 + `rot0;
	end
	else
	begin
      x1 <= x0 - y0;
      y1 <= y0 + x0;
      z1 <= z0 - `rot0;
	end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x2 <= 0; 						
		y2 <= 0;
		z2 <= 0;
	end
	else if(z1[31])
   begin
        x2 <= x1 + (y1 >>> 1);
        y2 <= y1 - (x1 >>> 1);
        z2 <= z1 + `rot1;
   end
   else
   begin
       x2 <= x1 - (y1 >>> 1);
       y2 <= y1 + (x1 >>> 1);
       z2 <= z1 - `rot1;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x3 <= 0; 						
		y3 <= 0;
		z3 <= 0;
	end
	else if(z2[31])
   begin
       x3 <= x2 + (y2 >>> 2);
       y3 <= y2 - (x2 >>> 2);
       z3 <= z2 + `rot2;
   end
   else
   begin
       x3 <= x2 - (y2 >>> 2);
       y3 <= y2 + (x2 >>> 2);
       z3 <= z2 - `rot2;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x4 <= 1'b0; 						
		y4 <= 1'b0;
		z4 <= 1'b0;
	end
	else if(z3[31])
   begin
       x4 <= x3 + (y3 >>> 3);
       y4 <= y3 - (x3 >>> 3);
       z4 <= z3 + `rot3;
   end
   else
   begin
       x4 <= x3 - (y3 >>> 3);
       y4 <= y3 + (x3 >>> 3);
       z4 <= z3 - `rot3;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x5 <= 1'b0; 						
		y5 <= 1'b0;
		z5 <= 1'b0;
	end
	else if(z4[31])
   begin
       x5 <= x4 + (y4 >>> 4);
       y5 <= y4 - (x4 >>> 4);
       z5 <= z4 + `rot4;
   end
   else
   begin
       x5 <= x4 - (y4 >>> 4);
       y5 <= y4 + (x4 >>> 4);
       z5 <= z4 - `rot4;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x6 <= 1'b0; 						
		y6 <= 1'b0;
		z6 <= 1'b0;
	end
	else if(z5[31])
   begin
       x6 <= x5 + (y5 >>> 5);
       y6 <= y5 - (x5 >>> 5);
       z6 <= z5 + `rot5;
   end
   else
   begin
       x6 <= x5 - (y5 >>> 5);
       y6 <= y5 + (x5 >>> 5);
       z6 <= z5 - `rot5;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x7 <= 1'b0; 						
		y7 <= 1'b0;
		z7 <= 1'b0;
	end
	else if(z6[31])
   begin
       x7 <= x6 + (y6 >>> 6);
       y7 <= y6 - (x6 >>> 6);
       z7 <= z6 + `rot6;
   end
   else
   begin
       x7 <= x6 - (y6 >>> 6);
       y7 <= y6 + (x6 >>> 6);
       z7 <= z6 - `rot6;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x8 <= 1'b0; 						
		y8 <= 1'b0;
		z8 <= 1'b0;
	end
	else if(z7[31])
   begin
       x8 <= x7 + (y7 >>> 7);
       y8 <= y7 - (x7 >>> 7);
       z8 <= z7 + `rot7;
   end
   else
   begin
       x8 <= x7 - (y7 >>> 7);
       y8 <= y7 + (x7 >>> 7);
       z8 <= z7 - `rot7;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x9 <= 1'b0; 						
		y9 <= 1'b0;
		z9 <= 1'b0;
	end
	else if(z8[31])
   begin
       x9 <= x8 + (y8 >>> 8);
       y9 <= y8 - (x8 >>> 8);
       z9 <= z8 + `rot8;
   end
   else
   begin
       x9 <= x8 - (y8 >>> 8);
       y9 <= y8 + (x8 >>> 8);
       z9 <= z8 - `rot8;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x10 <= 1'b0; 						
		y10 <= 1'b0;
		z10 <= 1'b0;
	end
	else if(z9[31])
   begin
       x10 <= x9 + (y9 >>> 9);
       y10 <= y9 - (x9 >>> 9);
       z10 <= z9 + `rot9;
   end
   else
   begin
       x10 <= x9 - (y9 >>> 9);
       y10 <= y9 + (x9 >>> 9);
       z10 <= z9 - `rot9;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x11 <= 1'b0; 						
		y11 <= 1'b0;
		z11 <= 1'b0;
	end
	else if(z10[31])
   begin
       x11 <= x10 + (y10 >>> 10);
       y11 <= y10 - (x10 >>> 10);
       z11 <= z10 + `rot10;
   end
   else
   begin
       x11 <= x10 - (y10 >>> 10);
       y11 <= y10 + (x10 >>> 10);
       z11 <= z10 - `rot10;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x12 <= 1'b0; 						
		y12 <= 1'b0;
		z12 <= 1'b0;
	end
	else if(z11[31])
   begin
       x12 <= x11 + (y11 >>> 11);
       y12 <= y11 - (x11 >>> 11);
       z12 <= z11 + `rot11;
   end
   else
   begin
       x12 <= x11 - (y11 >>> 11);
       y12 <= y11 + (x11 >>> 11);
       z12 <= z11 - `rot11;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x13 <= 1'b0; 						
		y13 <= 1'b0;
		z13 <= 1'b0;
	end
	else if(z12[31])
   begin
       x13 <= x12 + (y12 >>> 12);
       y13 <= y12 - (x12 >>> 12);
       z13 <= z12 + `rot12;
   end
   else
   begin
       x13 <= x12 - (y12 >>> 12);
       y13 <= y12 + (x12 >>> 12);
       z13 <= z12 - `rot12;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x14 <= 1'b0; 						
		y14 <= 1'b0;
		z14 <= 1'b0;
	end
	else if(z13[31])
   begin
       x14 <= x13 + (y13 >>> 13);
       y14 <= y13 - (x13 >>> 13);
       z14 <= z13 + `rot13;
   end
   else
   begin
       x14 <= x13 - (y13 >>> 13);
       y14 <= y13 + (x13 >>> 13);
       z14 <= z13 - `rot13;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x15 <= 1'b0; 						
		y15 <= 1'b0;
		z15 <= 1'b0;
	end
	else if(z14[31])
   begin
       x15 <= x14 + (y14 >>> 14);
       y15 <= y14 - (x14 >>> 14);
       z15 <= z14 + `rot14;
   end
   else
   begin
       x15 <= x14 - (y14 >>> 14);
       y15 <= y14 + (x14 >>> 14);
       z15 <= z14 - `rot14;
   end
end

always @ (posedge clk or negedge Rst_n)
begin
	if(!Rst_n)
	begin
		x16 <= 1'b0; 						
		y16 <= 1'b0;
		z16 <= 1'b0;
	end
	else if(z15[31])
   begin
       x16 <= x15 + (y15 >>> 15);
       y16 <= y15 - (x15 >>> 15);
       z16 <= z15 + `rot15;
   end
   else
   begin
       x16 <= x15 - (y15 >>> 15);
       y16 <= y15 + (x15 >>> 15);
       z16 <= z15 - `rot15;
   end
end	
	
	always@(posedge clk or negedge Rst_n)
	if(!Rst_n)
	begin
		Quadrant[0] <= 0;
		Quadrant[1] <= 0;
		Quadrant[2] <= 0;
		Quadrant[3] <= 0;
		Quadrant[4] <= 0;
		Quadrant[5] <= 0;
		Quadrant[6] <= 0;
		Quadrant[7] <= 0;
		Quadrant[8] <= 0;
		Quadrant[9] <= 0;
		Quadrant[10] <= 0;
		Quadrant[11] <= 0;
		Quadrant[12] <= 0;
		Quadrant[13] <= 0;
		Quadrant[14] <= 0;
		Quadrant[15] <= 0;
		Quadrant[16] <= 0;
	end
	else begin
		Quadrant[0] <= phase[17:16];//phase[17:16]为角象限
		Quadrant[1] <= Quadrant[0];
		Quadrant[2] <= Quadrant[1];
		Quadrant[3] <= Quadrant[2];
		Quadrant[4] <= Quadrant[3];
		Quadrant[5] <= Quadrant[4];
		Quadrant[6] <= Quadrant[5];
		Quadrant[7] <= Quadrant[6];
		Quadrant[8] <= Quadrant[7];
		Quadrant[9] <= Quadrant[8];
		Quadrant[10] <= Quadrant[9];
		Quadrant[11] <= Quadrant[10];
		Quadrant[12] <= Quadrant[11];
		Quadrant[13] <= Quadrant[12];
		Quadrant[14] <= Quadrant[13];
		Quadrant[15] <= Quadrant[14];
		Quadrant[16] <= Quadrant[15];
	end
	
	//输出判定,奇变偶不变,符好看象限
	always@(posedge clk or  negedge Rst_n)
	if(!Rst_n)
	begin
		Sinx <= 0;
		Cosx <= 0;
		error <= 0;
	end
	else begin 
		error <= z16;
	case	(Quadrant[16])
		2'b00: begin  //第一象限
			Sinx <= y16;
			Cosx <= x16;
		end
			
		2'b01: begin  //第二象限
			Sinx <= x16;
			Cosx <= ~(y16) + 1'b1;
		end
		
		2'b10: begin  //第三象限
			Sinx <= ~(y16) + 1'b1;
			Cosx <= ~(x16) + 1'b1;
		end
		
		2'b11: begin  //第四象限
			Sinx <= ~(x16) + 1'b1;
			Cosx <= y16;
		end
		endcase
	end
endmodule

//仿真文件testbench

`timescale 1ns/1ns
`define clk_period 20

module Cordic_design_tb;

    reg clk;
    reg Rst_n;
    reg [31:0]phase;
    wire  signed [31:0] Sinx;
    wire  signed [31:0] Cosx;
    wire  signed [31:0] error;
    
    Cordic_design Cordic_design(

        .clk(clk),
        .Rst_n(Rst_n),
        .Sinx(Sinx),
        .Cosx(Cosx),
        .phase(phase),
        .error(error)
    );


    
    
    reg [15:0]cnt;
    reg [31:0]phase_n;
    reg [15:0]cnt_n;
    initial clk = 1;
    always#(`clk_period / 2) clk = ~clk;
    initial begin
    Rst_n = 0;
    #20;
    Rst_n = 1;
    #(`clk_period *10000);
    $stop;
    end
    
    always@(posedge clk or negedge Rst_n)
    if(!Rst_n)
        cnt <= 16'd0;
    else cnt <= cnt_n;
    
    always@(*)
    if(cnt == 16'd359)
        cnt_n <= 0;
    else cnt_n <= cnt + 1'b1;
    
    always@(*)
    if(!Rst_n)
        phase_n <= 0;
    else if(cnt >= 16'd0 && cnt <= 16'd90)
        phase_n <= {2'b00,cnt};
    else if(cnt > 16'd90 && cnt < 16'd180)
        phase_n <= {2'b01,cnt - 16'd90};
    else if(cnt > 16'd180 && cnt <=16'd270)
        phase_n <= {2'b10,cnt - 16'd180};
    else if(cnt > 16'd270)
        phase_n <= {2'b11, cnt - 16'd270};
        
    always@(posedge clk or negedge Rst_n)
    if(!Rst_n)
        phase <= 0;
    else phase <= phase_n;
    
endmodule

仿真结果如下
输出的正弦余弦波形
在fpga中用Cordic算法来产生正弦函数_第4张图片

波形的幅值在65535与-65535之间,由于数据在处理时时放大了2^ 16倍,对应输出也扩大了2^16倍,所以结果正确

在这里插入图片描述在这里插入图片描述
当输的角度值为90度时,17个时钟周期过后,Sinx输出对应的结果65535,时间正确
在这里插入图片描述
参考文章
链接: https://blog.csdn.net/u010712012/article/details/77755567

你可能感兴趣的:(FPGA,Cordic,算法)