| |
上一篇文章中,用的是组合逻辑加一级寄存器来实现cordic的三角运算。虽然,是可行的。但是在实际中,由于组合逻辑计算单元的延迟较大,造成系统的运行最大时钟频率不高。因此就不能高速的处理。
而流水线结构,就是将有较大延迟的组合逻辑,分解成几个延迟小的组合逻辑。这样的话,可以大大提高系统的时钟频率。但是,由于分解了,因此,数据运算不是马上就可得到,而是要延迟几个周期才可得到运算的值。而延迟的周期是由设计的几级流水线决定的。
将上次实现的硬件结构,改成流水线的结构。
其代码如下:
module
cordic_tri_func
#(
//输出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[4][W-1:4]};
y[5] <= y[4] - {{4{x[4][W-1]}},x[4][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
认真观察一下代码,发现这流水线结构的代码,和上次组合逻辑机构实现的代码基本上是一样的。唯一不一样的就是赋值的不同。组合逻辑结构用的是=赋值,而流水线结构,用的是=>赋值。
关于=和=>的区别,大家可以查查相关资料。前个是阻塞赋值,也就是赋值后,值就马上变化。而后者是非阻塞赋值,赋值后,不是马上就改变。关于这个非阻塞和阻塞的概念,是很重要的,各位要去查查相关资料了解一下,了解他们之间的区别。这里就不详细说了。
(主要是要画图才能讲得清楚,本人比较懒,难得画图)
接下来,就是仿真了。要有仿真的思想,在学习verilog的时候,每当写完一个模块,就记得要编写测试文件去仿真写的模块,然后验证看自己写的模块对不对。这样才能提升自己的硬件编程的水平。在高级点,就是边写程序,边想对应实现的硬件是什么,然后再自己想这实现的硬件是不是能实现我们要求的功能。这个属于高级了,我也不是很会,不过现在在开始培养。大家也可以这样试一下。毕竟,硬件和软件还是不一样的。
测试代码和上次的代码一样,仿真图,如下所示:
这大家可以看出,仿真的开始阶段,输出都是未定态(X)。因为这里采用的是7级流水线,因此,在7个时钟周期后,才开始输出真正的计算出来的值。
上面的编程其实是不规范的。流水线,编程,最好是,每一级单独用一个always来编写。而且还要加入复位信号。这样才是一个好的代码了。