您的位置:首页 > 其它

verilog实现:比流水线法更简单的cordic算法

2018-01-06 23:22 211 查看
cordic原理不再赘诉

传统的流水线法有两个缺点:

1.代码冗长,16段流水线意味着要写16段很相近的代码,我自己在写的过程中都觉得十分疲惫。各位博主的代码也基本都长达380+行。

2.多段流水会带来较长周期的时延。以16段流水为例,如果不考虑预处理,则时延长达16周期,若考虑第一个周期的预处理,则时延为17周期。

流水线的方式去实现有两种方法:

1.手动实现右移操作,例如:   x2 <= x1 + {y1[16],y1[16:1]};

通过算术右移来实现,即: x2 <= x1 + (y1 >>> 1);  但是算术右移操作需要将寄存器定义为有符号型(即在reg或wire后增加signed)。

`timescale 1 ns/1ns
module try_cordic( sin,cos, phase_in,clk);
output reg signed [16:0]sin,cos;          // 17位输出,sin输出正弦信号,cos输出余弦信号
input [15:0]phase_in;                 //  16位输入
input clk;                       // 时钟信号
parameter K = 16'h9b74;          // 0.60725*2^16 ,把小数扩大2^16来减小误差
reg signed [16:0] x0=0,y0=0;          //因为使用算术移位,因此需要定义有符号型
reg signed [16:0] temp1=0,temp2=0;      // 作临时变量使用
reg signed [16:0] z=0;
reg [1:0] quadrant;                  //象限
reg [15:0]rot[15:0];                     //存储每次旋转的角度
integer i;
initial
begin
temp1=K;
temp2=0;
rot[0]=16'h2000 ;   //45
rot[1]=16'h12e4;    //26.5651
rot[2]=16'h09fb ;   //14.0362
rot[3]=16'h0511 ;   //7.1250
rot[4]=16'h028b ;   //3.5763
rot[5]=16'h0145 ;   //1.7899
rot[6]=16'h00a3 ;   //0.8952
rot[7]=16'h0051 ;   //0.4476
rot[8]=16'h0028 ;   //0.2238
rot[9]=16'h0014 ;   //0.1119
rot[10]=16'h000a ;   //0.0560
rot[11]=16'h0005 ;   //0.0280
rot[12]=16'h0003 ;   //0.0140
rot[13]=16'h0001  ;  //0.0070
rot[14]=16'h0001 ;   //0.0035
rot[15]=16'h0000  ;  //0.0018
end

always @(posedge clk)
begin
quadrant=phase_in[15:14];       //  输入的前两位表示象限
z={3'b0,phase_in[13:0]};          //  z用于存储变换到第一象限后的角度
for(i=0;i<16;i=i+1)            //迭代更新16次
begin
if(z[16])                            //  如果角度小于0,即旋转角度过大
begin
x0 = temp1+(temp2>>>i);           //使用算术右移需要带括号,因为优先级低于+号
y0 = temp2-(temp1>>>i);
z = z+rot[i];
end
else                               //如果旋转角度不够
begin
x0 = temp1-(temp2>>>i);
y0 = temp2+(temp1>>>i);
z = z-rot[i];
end
temp1=x0;            //每循环一次需要把当前值赋给temp
temp2=y0;
end
temp1=K;               //16次迭代完成,temp重新置位,方便下个角度的计算
temp2=0;

// 根据角度的象限作相应变换
case(quadrant)
2'b00:begin                //第一象限
cos <= x0;
sin <= y0;
end
2'b01:begin           //第二象限
cos <= ~(y0) + 1'b1;    //-sin
sin <= x0;           //cos
end
2'b10:begin           //第三象限
cos <= ~(x0) + 1'b1;             //-cos
sin <= ~(y0) + 1'b1;          //-sin
end
2'b11:begin            //第四象限
cos <= y0;               //sin
sin <= ~(x0) + 1'b1;             //-cos
end
endcase
end
endmodule
分析网上一直使用流水线法的原因:简化版本用到了算术移位,而算术移位是2001年才增加进来的,有些博主写的时候可能早在2001年以前,在那时恐怕只有流水线的方式才能实现。而后来的博主可能只是简单的模仿,并没有深究其他的方法。

此外,如果将代码中的阻塞赋值变为非阻塞赋值 也能完成,但是会增加1个时钟周期时延。

本人新学verilog 欢迎讨论!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  verilog cordic