在fpga中实现正弦函数可有三种基本方法,Cordic法和查找表法和线性插值法,三种方法各有其优劣性,今天就使用Cordic算法来产生正弦函数
CORDIC ( Coordinate Rotation Digital Computer ) 是坐标旋转数字计算机算法的简称,由 Vloder• 于 1959 年在设计美国航空导航控制系统的过程中首先提出, 主要用于解决导航系统中三角函数、 反三角函数和开方等运算的实时计算问题。 1971 年, Walther 将圆周系统、 线性系统和双曲系统统一到一个 CORDIC 迭代方程里 , 从而提出了一种统一的CORDIC 算法形式
Cordic算法是一种化繁为简的算法,在运算时,仅需要进行移位和加法的迭代操作,Cordic有两种计算模式,分别为旋转模式和向量模式,可分别在三种坐标系中来进行使用,对应可衍生8种计算模式,然后这8种模式相结合可衍生更多算法
今天就介绍圆坐标系中的旋转模式来产生正弦余弦函数
在圆坐标中,设初始点为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
将这个方程式称为伪旋转,旋转的角度是正确的,但因为模值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级流水线设计,每一级流水线需进行三次加法运算,两次移位操作和进行一次查找表
这里进行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
波形的幅值在65535与-65535之间,由于数据在处理时时放大了2^ 16倍,对应输出也扩大了2^16倍,所以结果正确
当输的角度值为90度时,17个时钟周期过后,Sinx输出对应的结果65535,时间正确
参考文章
链接: https://blog.csdn.net/u010712012/article/details/77755567