接着上篇的分析。
接下来就是编写代码:
module cdcd
#(
//输出cos和sin的位宽
//最高位为符号位,剩下6位为数据位
parameter W = 17,
//输入的角度采用16位表示
//最高位表示符号位,1为负数。0为正数
//接下来的7位,表示角度的整数,可表示范围为0--127
//最后的8位,表示小数。可表示范围为0--1
parameter N_Z = 16 //输入角度phi的位宽
)
(
input clk,
input [N_Z-1:0] phi,
output [W-1:0] cos,
output [W-1:0] sin
);
//定义中间保存迭代的寄存器值
reg [W-1:0] x [7:0];
reg [W-1:0] y [7:0];
reg [N_Z-1:0] z [7:0];
always@( posedge clk ) begin
x[0] = 17'd39797; //0.60725
y[0] = 0;
z[0] = phi;
//迭代第一次,角度偏移45°
if(z[0][N_Z-1])
begin
x[1] = x[0] + y[0];
y[1] = y[0] - x[0];
z[1] = z[0] + 16'd11520; //45° = 0_0101101_00000000 = 11520
end
else
begin
x[1] = x[0] - y[0];
y[1] = y[0] + x[0];
z[1] = z[0] - 16'd11520;
end
//迭代第二次,角度偏移26.56°
if(z[1][N_Z-1])
begin
x[2] = x[1] + {{1{y[1][W-1]}},y[1][W-1:1]};
y[2] = y[1] - {{1{x[1][W-1]}},x[1][W-1:1]};
z[2] = z[1] + 16'd6799; //25.56° = 0_0011010_10001111 = 6799
end
else
begin
x[2] = x[1] - {{1{y[1][W-1]}},y[1][W-1:1]};
y[2] = y[1] + {{1{x[1][W-1]}},x[1][W-1:1]};
z[2] = z[1] - 16'd6799;
end
//迭代第三次,角度偏移14.04°
if(z[2][N_Z-1])
begin
x[3] = x[2] + {{2{y[2][W-1]}},y[2][W-1:2]};
y[3] = y[2] - {{2{x[2][W-1]}},x[2][W-1:2]};
z[3] = z[2] + 16'd3594; //14.04° = 0_0001110_00001010 = 3594
end
else
begin
x[3] = x[2] - {{2{y[2][W-1]}},y[2][W-1:2]};
y[3] = y[2] + {{2{x[2][W-1]}},x[2][W-1:2]};
z[3] = z[2] - 16'd3594;
end
//迭代第四次,角度偏移7.13°
if(z[3][N_Z-1])
begin
x[4] = x[3] + {{3{y[3][W-1]}},y[3][W-1:3]};
y[4] = y[3] - {{3{x[3][W-1]}},x[3][W-1:3]};
z[4] = z[3] + 16'd1825; //7.13° = 0_0000111_00100001 = 1825
end
else
begin
x[4] = x[3] - {{3{y[3][W-1]}},y[3][W-1:3]};
y[4] = y[3] + {{3{x[3][W-1]}},x[3][W-1:3]};
z[4] = z[3] - 16'd1825;
end
//迭代第五次,角度偏移3.58°
if(z[4][N_Z-1])
begin
x[5] = x[4] + {{4{y[4][W-1]}},y[2][W-1:4]};
y[5] = y[4] - {{4{x[4][W-1]}},x[2][W-1:4]};
z[5] = z[4] + 16'd916; //3.58° = 0_0000011_10010100 = 916
end
else
begin
x[5] = x[4] - {{4{y[4][W-1]}},y[4][W-1:4]};
y[5] = y[4] + {{4{x[4][W-1]}},x[4][W-1:4]};
z[5] = z[4] - 16'd916;
end
//迭代第六次,角度偏移1.79°
if(z[5][N_Z-1])
begin
x[6] = x[5] + {{5{y[5][W-1]}},y[5][W-1:5]};
y[6] = y[5] - {{5{x[5][W-1]}},x[5][W-1:5]};
z[6] = z[5] + 16'd458; //1.79° = 0_0000001_11001010 = 458
end
else
begin
x[6] = x[5] - {{5{y[5][W-1]}},y[5][W-1:5]};
y[6] = y[5] + {{5{x[5][W-1]}},x[5][W-1:5]};
z[6] = z[5] - 16'd458;
end
//迭代第七次,角度偏移0.9°
if(z[6][N_Z-1])
begin
x[7] = x[6] + {{6{y[6][W-1]}},y[6][W-1:6]};
y[7] = y[6] - {{6{x[6][W-1]}},x[6][W-1:6]};
z[7] = z[6] + 16'd230; //0.9° = 0_0000000_11100110 = 230
end
else
begin
x[7] = x[6] - {{6{y[6][W-1]}},y[6][W-1:6]};
y[7] = y[6] + {{6{x[6][W-1]}},x[6][W-1:6]};
z[7] = z[6] - 16'd230;
end
end
assign cos = x[7];
assign sin = y[7];
endmodule
理解cordic后,编写代码还是比较容易的。编写测试代码,测试该程序:
module cdcd_tb;
//
Inputs
reg
clk;
reg
[15:0] phi;
//
Outputs
wire
[16:0] cos;
wire
[16:0] sin;
//
Instantiate the Unit Under Test (UUT)
cdcd
uut (
.clk(clk),
.phi(phi),
.cos(cos),
.sin(sin)
);
always
#5 clk = ~clk;
initial
begin
//
Initialize Inputs
clk
= 0;
phi
= 0;
while(1)
begin
@(negedge
clk)
phi = (phi + (5<<8));
if( 90 <= (phi>>8))
phi = 0;
end
endmodule
用ISE自带的ISIM仿真,如下图所示:
选取一个值来检查:
选取,输入的角度为7680。。这是二进制显示,实际的角度为7680/256= 30°。
运算得出的结果
X =
cos(30°) = 56858/65536 = 0.867484
Y =
sin(30°) = 32585/65536 = 0.497208
与实际值相比较:
cos(30°) = 0.866025403784439
sin(30°) = 0.5
可看出,相比较,运算出来的结果还是有一定误差的。不过在允许的误差内,还是可以用的。当然可以增加迭代次数,这样可以减小误差。
以上实现,可看出,在一个时钟周期内,就可以算出结果了。但是因为这是功能仿真,没有考虑到计算的延时。如果是用时序仿真的话,就可以看出,输出结果和输入结果有一定的延时。由于中间的运算是采用组合逻辑实现的,因此运算的延时还是挺大的,这样的话,就会影响系统运行的最大频率了。
因此,在实际中,这样是不实用的。因此,后面可以采用流水线的结构,来进行运算,这样可以使系统的运行最大频率提高,还能在一个周期内算出结果。
以上,就是我对cordic算法的硬件实现的分析了。下次,分析采用流水线结构来实现三角函数。