系列文章目录
文章目录
- 系列文章目录
- 一、什么是CORDIC算法?
- 二、CORDIC算法原理推导
- 三、CORDIC模式
- 3.1 旋转模式
- 3.2 向量模式
- 四、Verilog实现CORDIC
- 4.1 判断象限
- 4.2 定义角度表
- 4.3 迭代公式
- 五、仿真验证
- 5.1 matlab打印各角度的正余弦值
- 5.2 Verilog仿真结果观察
- 六、Xilinx CORDIC IP核介绍
- 6.1 Xilinx CORDIC IP 特点
- 6.2 端口说明
- 6.3 AXI4_stream握手信号
- 6.4 数据传输模式
- 6.5 数据结构
- 6.5.1 输入数据结构
- 6.5.2 输出数据结构
- 6.6 各种功能描述
- 6.6.1 矢量旋转(Rotate)
- 6.6.2 坐标转换(Translate)
- 6.6.3 sin 和cos
- 6.6.4 Sinh and Cosh
- 6.6.5 ArcTan
- 6.6.6 ArcTanh
- 6.6.7 Square Root
- 6.7 输出输入数据的表示
- 七、例化IP
- 7.1 仿真代码
- 7.2 仿真结果
一、什么是CORDIC算法?
CORDIC(COordinate Rotation DIgital Computer)算法是一种用于计算三角函数、双曲函数、对数、指数和平方根等数学函数的迭代算法。对于硬件电路来说,计算三角函数不像CPU那样方便,如果使用大量的乘法器和加法器,那么整个硬件电路会消耗很多资源,因此cordic算法就有效地解决了乘法器和加法器的问题,它的主要优点是:可以使用简单的加法和位移操作,而不需要乘法和除法,从而适合在硬件中实现三角函数的计算,特别是数字信号处理(DSP)和嵌入式系统中。
二、CORDIC算法原理推导
CORDIC算法的基本原理是:将旋转角度 θ θ θ分成多个连续的小偏转角,通过逐次摇摆逼近目标旋转角度来完成旋转过程。
如上图所示:在直角坐标系内将点(
x
1
,
y
1
x_1,y_1
x1,y1)逆时针旋转
θ
θ
θ至点(
x
2
x_2
x2,
y
2
y_2
y2),那么怎么计算
c
o
s
θ
cosθ
cosθ和
s
i
n
θ
sinθ
sinθ的值呢?
我们知道:
{
x
1
=
cos
φ
y
1
=
sin
φ
\left\{ \begin{aligned} x_1 & = \cosφ \\ y_1 & = \sinφ \\ \end{aligned} \right.
{x1y1=cosφ=sinφ
{ x 2 = cos ( θ + φ ) y 2 = sin ( θ + φ ) \left\{ \begin{aligned} x_2& = \cos(θ+φ) \\ y_2 & = \sin(θ+φ)\\ \end{aligned} \right. {x2y2=cos(θ+φ)=sin(θ+φ)
根据三角函数和差公式,我们可以得到:
{
x
2
=
cos
θ
c
o
s
φ
−
s
i
n
θ
s
i
n
φ
y
2
=
s
i
n
θ
c
o
s
φ
+
c
o
s
θ
s
i
n
φ
\left\{ \begin{aligned} x_2& = \cosθcosφ - sinθsinφ\\ y_2 & = \ sinθcosφ + cosθsinφ\\ \end{aligned} \right.
{x2y2=cosθcosφ−sinθsinφ= sinθcosφ+cosθsinφ
再用
x
1
x_1
x1替换掉
c
o
s
φ
cosφ
cosφ,
y
1
y_1
y1替换掉
s
i
n
φ
sinφ
sinφ可以得到:
{
x
2
=
c
o
s
θ
∗
x
1
−
s
i
n
θ
∗
y
1
y
2
=
s
i
n
θ
∗
x
1
+
c
o
s
θ
∗
y
1
\left\{ \begin{aligned} x_2& = \ cosθ *x_1 - sinθ *y_1\\ y_2 & = \ sinθ*x_1 + cosθ *y_1\\ \end{aligned} \right.
{x2y2= cosθ∗x1−sinθ∗y1= sinθ∗x1+cosθ∗y1
也可以写成矩阵形式:
[
x
2
y
2
]
=
[
c
o
s
θ
−
s
i
n
θ
s
i
n
θ
c
o
s
θ
]
[
x
1
y
1
]
\begin{gathered} \begin{bmatrix} x_2 \\ y_2 \end{bmatrix} = \quad \begin{bmatrix} cosθ & - sinθ \\ sinθ & cosθ \end{bmatrix} \begin{bmatrix} x_1 \\ y_1 \end{bmatrix} \quad \end{gathered}
[x2y2]=[cosθsinθ−sinθcosθ][x1y1]
提取公因子
c
o
s
θ
cosθ
cosθ:
[
x
2
y
2
]
=
c
o
s
θ
[
1
−
t
a
n
θ
t
a
n
θ
1
]
[
x
1
y
1
]
\begin{gathered} \begin{bmatrix} x_2 \\ y_2 \end{bmatrix} = cosθ \quad \begin{bmatrix} 1& - tanθ \\ tanθ & 1 \end{bmatrix} \begin{bmatrix} x_1 \\ y_1 \end{bmatrix} \quad \end{gathered}
[x2y2]=cosθ[1tanθ−tanθ1][x1y1]
{ x 2 = c o s θ ( x 1 − t a n θ ∗ y 1 ) y 2 = c o s θ ( t a n θ ∗ x 1 + y 1 ) \left\{ \begin{aligned} x_2& = \ cosθ(x_1 - tanθ *y_1)\\ y_2 & = \ cosθ(tanθ*x_1 + y_1)\\ \end{aligned} \right. {x2y2= cosθ(x1−tanθ∗y1)= cosθ(tanθ∗x1+y1)
这个公式就能将点(
x
1
,
y
1
x_1,y_1
x1,y1)逆时针旋转
θ
θ
θ至点(
x
2
x_2
x2,
y
2
y_2
y2)。因为旋转的θ是个固定值,因此
c
o
s
θ
cosθ
cosθ是一个常数,我们暂时去掉
c
o
s
θ
cosθ
cosθ可以得到伪旋转方程:
{
x
2
=
x
1
−
t
a
n
θ
∗
y
1
y
2
=
y
1
+
t
a
n
θ
∗
x
1
\left\{ \begin{aligned} x_2& = \ x_1 - tanθ *y_1\\ y_2 & = \ y_1 + tanθ*x_1 \\ \end{aligned} \right.
{x2y2= x1−tanθ∗y1= y1+tanθ∗x1
伪旋转方程意思就是,通过伪旋转方程我们可以将点( x 1 , y 1 x_1,y_1 x1,y1)逆时针旋转 θ θ θ角,但是模长不是我们想要的模长。简而言之就是伪旋转方程负责旋转到正确的角度,去掉的 c o s θ cosθ cosθ能使得模长为想要的长度,示意图如下所示:
因此经过伪旋转后到了点
(
x
2
^
,
y
2
^
)
(\hat{x_2},\hat{y_2})
(x2^,y2^),向量R的模值增加了
1
/
c
o
s
θ
1/cosθ
1/cosθ倍。即:向量旋转到了正确的角度,但是模值不对。CORDIC的核心思想就是:先旋转到正确的角度,最后再对模长进行补偿。 我们前面也说了硬件电路不擅长乘除法运算,但是擅长加减法运算。这个伪旋转方程里还是有乘法以及
t
a
n
θ
tanθ
tanθ,我们就要做一种处理:
如果
t
a
n
θ
tanθ
tanθ刚好等于1/2,即θ角=45°,那么这个伪旋转方程就变成了
{
x
2
=
x
1
−
y
1
/
2
y
2
=
y
1
+
x
1
/
2
\left\{ \begin{aligned} x_2& = \ x_1 - y_1/2\\ y_2 & = \ y_1 + x_1/2 \\ \end{aligned} \right.
{x2y2= x1−y1/2= y1+x1/2
在硬件电路里,÷2可以用右移一位表示。这样整个方程就变成了只有加减法和移位操作了,因此,我们可以让θ为一系列固定的角度,使得
t
a
n
θ
i
tanθ^{i}
tanθi =
2
−
i
2^{-i}
2−i,这样经过一系列旋转后,总能找到一个角度无限逼近与我们想要的角度,这就是CORDIC算法的最核心的思想,故伪旋转方程变为:
{
x
2
^
=
x
1
−
y
1
∗
t
a
n
θ
=
x
1
−
y
1
∗
2
−
i
y
2
^
=
y
1
+
x
1
∗
t
a
n
θ
=
y
1
+
x
1
∗
2
−
i
\left\{ \begin{aligned} \hat{x_2}& = \ x_1 - y_1*tanθ = x_1 - y_1*2^{-i}\\ \hat{y_2} & = \ y_1 + x_1*tanθ =y_1 + x_1* 2^{-i} \\ \end{aligned} \right.
{x2^y2^= x1−y1∗tanθ=x1−y1∗2−i= y1+x1∗tanθ=y1+x1∗2−i
其中 t a n θ i tanθ^{i} tanθi对应的角度值表如下所示:
这里把旋转角度变换改成了迭代运算。将各种可能的旋转角度进行一些条件限制,可以使得对任意旋转角度都能够通过连续的小角度的迭代来完成。旋转迭代顺序为:
- 第一次迭代旋转的角度是45.0°
- 第二次迭代旋转的角度是26.555°
- 第三次迭代旋转的角度是14.036°
- 第四次迭代旋转的角度是7.125°
- 第五次迭代旋转的角度是3.576°
- 第六次迭代旋转的角度是1.789°
以此类推,迭代次数越多,精度越高,越能逼近想要的角度θ。在迭代到一定次数后,我们角度已经旋转到正确位置了,现在回头来看我们舍掉的 c o s θ cos θ cosθ,这是确定我们模长的因子,由上表也能看出,当θ值越小时, c o s θ cosθ cosθ无限接近1。所以我们迭代过程中舍掉的 c o s θ 1 ∗ c o s θ 2 ∗ c o s θ 3 ∗ c o s θ 4 ∗ c o s θ 5 ∗ c o s θ 6 ∗ c o s θ 7 ∗ c o s θ 8 . . . . . . . . 一定是收敛某个数的 cosθ^{1}*cosθ^{2}*cosθ^{3}*cosθ^{4}*cosθ^{5}*cosθ^{6}*cosθ^{7}*cosθ^{8}........一定是收敛某个数的 cosθ1∗cosθ2∗cosθ3∗cosθ4∗cosθ5∗cosθ6∗cosθ7∗cosθ8........一定是收敛某个数的
经过计算,在迭代16次以后,上面的乘积≈0.60725941;1/0.60725941 ≈1.64676024187 。;在进行伪旋转之后,需要对伪旋转后的幅度进行标定,所以伪旋转后的坐标还需要乘以一个系数0.60725941才能得到我们想要的正确坐标。
最终CORDIC算法伪旋转的公式为:
{
x
i
+
1
=
x
i
−
d
i
∗
y
i
∗
∗
2
−
i
y
i
+
1
=
y
i
+
d
i
∗
x
i
∗
∗
2
−
i
z
i
+
1
=
z
i
−
d
i
θ
i
\left\{ \begin{aligned} x^{i+1}& = \ x^{i} -d_{i}* y^{i}**2^{-i} \\ y^{i+1} & = \ y^{i} + d_{i} * x^{i}**2^{-i} \\ z^{i+1} & = \ z^{i} - d_{i}θ^{i} \\ \end{aligned} \right.
⎩
⎨
⎧xi+1yi+1zi+1= xi−di∗yi∗∗2−i= yi+di∗xi∗∗2−i= zi−diθi
其中
d
i
d_{i}
di表示旋转因子,
d
i
d_{i}
di=±1,+1表示逆时针旋转,-1表示顺时针旋转。因为伪旋转不是最终的旋转,还有前面的
c
o
s
θ
cosθ
cosθ的累乘称为缩放因子,伪旋转后的坐标伸缩了目标坐标的Kn倍,其中Kn:
当确定迭代次数n时,可以预先计算出缩放因子Kn。当n趋近于正无穷时,Kn趋近于1.64676024187,1/Kn趋近于0.60725941。前面通过省略
c
o
s
θ
cosθ
cosθ来进行伪旋转,使得在n次迭代后得到
{
x
伪
=
K
n
(
x
0
c
o
s
θ
−
y
0
s
i
n
θ
)
,
x
n
=
x
伪
∗
0.60725941
y
伪
=
K
n
(
y
0
c
o
s
θ
+
x
0
s
i
n
θ
)
,
y
n
=
y
伪
∗
0.60725941
z
n
=
0
\left\{ \begin{aligned} x_{伪}& = \ K_{n}(x_0cosθ - y_{0}sinθ) ,x_{n} = x_{伪} *0.60725941\\ y_{伪} & = \ K_{n}(y_0cosθ + x_{0}sinθ) ,y_{n} = y_{伪} *0.60725941 \\ z_{n} & = \ 0 \\ \end{aligned} \right.
⎩
⎨
⎧x伪y伪zn= Kn(x0cosθ−y0sinθ),xn=x伪∗0.60725941= Kn(y0cosθ+x0sinθ),yn=y伪∗0.60725941= 0
也可以设置
x
0
=
0.0.60725941
,
y
0
=
0
x_0= 0.0.60725941,y_0= 0
x0=0.0.60725941,y0=0,来消除伸缩因子,从而得到:
{
x
n
=
c
o
s
θ
y
n
=
s
i
n
θ
\left\{ \begin{aligned} x_{n} = cosθ\\ y_{n} = sinθ\\ \end{aligned} \right.
{xn=cosθyn=sinθ
三、CORDIC模式
3.1 旋转模式
旋转模式就是,给定一个角度θ和初始坐标,算出旋转后的坐标。旋转模式下,旋转方向是以剩余角度
z
i
+
1
z^{i+1}
zi+1的符号作为每次迭代旋转方向的依据。在旋转模式下,等迭代完成后,
z
i
+
1
趋近于
0
z^{i+1}趋近于0
zi+1趋近于0
,
x
i
+
1
=
c
o
s
θ
,x^{i+1} = cosθ
,xi+1=cosθ,
y
i
+
1
=
s
i
n
θ
y^{i+1} = sinθ
yi+1=sinθ。
例如,输入向量为
(
x
0
,
y
0
)
(x_0,y_0)
(x0,y0)输入角度为
z
z
z,经过迭代旋转后,剩余角度为0,则输出目标向量为
(
x
c
o
s
z
−
y
s
i
n
z
)
/
K
n
(xcosz-ysinz)/K_n
(xcosz−ysinz)/Kn,
(
y
c
o
s
z
+
x
s
i
n
z
)
/
K
n
(ycosz+xsinz)/K_n
(ycosz+xsinz)/Kn其中nK为缩放因子。当旋转角度为30°时,其迭代过程如下:
s
i
n
30
°
=
0.5006
,
c
o
s
30
°
=
0.8657
sin30° = 0.5006,cos30°=0.8657
sin30°=0.5006,cos30°=0.8657
3.2 向量模式
向量模式就是给一个坐标(x,y),让向量旋转到x轴上,当y趋近于0时候,向量就无限接近x轴,此时x的坐标就是向量的模长,θ等于arctan(y/x) 。CORDIC算法的向量模式可以计算出输入向量的幅度值。并且使用向量模式就是使旋转后的向量与x轴重合。因此,向量的幅度就是旋转向量的x值,幅度结果由Kn增益标定。向量模式主要是用来求反正切、反正弦、反余弦以及向量幅值等
四、Verilog实现CORDIC
4.1 判断象限
由迭代公式可以看出,每次迭代的范围是±(45 + 26.6 + 14 +… )≈ ±99.8°,因此我们要将输入的象限通过三角变换到第一和第四象限,例如
c
o
s
150
°
=
−
c
o
s
30
°
,
s
i
n
150
°
=
s
i
n
30
°
cos150° = -cos30°, sin150° = sin30°
cos150°=−cos30°,sin150°=sin30°
所以首先要对输入的角度值判断:
always @(*) begin
if((w_act == 1'b1) && (i_angle >=32'd0) && (i_angle < 32'd900000)) //[0:90]第一象限
w_quadrant <= 2'd0;
else if((w_act == 1'b1) && (i_angle >=32'd900000) && (i_angle <= 32'd1800000)) //[90:180]第二象限
w_quadrant <= 2'd1;
else if((w_act == 1'b1) && (i_angle >=-32'd1800000) && (i_angle < -32'd900000))[-180:-90]第三象限
w_quadrant <= 2'd2;
else if(w_act == 1'b1) [-90:0]第四象限
w_quadrant <= 2'd3;
end
然后再根据象限对角度进行处理,±180°:
if(w_quadrant == 2'd1) //第二象限就减180°
r_zi <= i_angle - 32'd1800000;
else if(w_quadrant == 2'd2) //第三象限就+180°
r_zi <= i_angle + 32'd1800000;
else
r_zi <= i_angle;
最后根据象限判断输出的正负值
assign o_sin = ((w_quadrant == 2'd1)||(w_quadrant == 2'd2)) ? -ro_sin : ro_sin;//根据象限输出正确的数值
assign o_cos = ((w_quadrant == 2'd2)||(w_quadrant == 2'd3)) ? -ro_cos : ro_cos;//根据象限输出正确的数值
4.2 定义角度表
由上面公式可以知道,我们必须提前将45°,26.565°…等等存储起来,然后每次按照顺序旋转角度:
//角度扩大了10000倍
wire signed [31:0] w_arctan[0:15] ; //角度值表
assign w_arctan[0 ] = 32'd450000 ; //arctan45° = 1
assign w_arctan[1 ] = 32'd265650 ; //arctan26.565° = 1/2
assign w_arctan[2 ] = 32'd140362 ; //arctan14.036° = 1/4
assign w_arctan[3 ] = 32'd71250 ; //arctan7.1250° = 1/8
assign w_arctan[4 ] = 32'd35763 ; //arctan3.5763° = 1/16
assign w_arctan[5 ] = 32'd17899 ; //arctan1.7899° = 1/32
assign w_arctan[6 ] = 32'd8951 ; //arctan0.8951° = 1/64
assign w_arctan[7 ] = 32'd4476 ; //arctan0.4476° = 1/128
assign w_arctan[8 ] = 32'd2238 ; //arctan0.2238° = 1/256
assign w_arctan[9 ] = 32'd1119 ; //arctan0.1119° = 1/512
assign w_arctan[10] = 32'd559 ; //arctan0.0559° = 1/1024
assign w_arctan[11] = 32'd279 ; //arctan0.0279° = 1/2048
assign w_arctan[12] = 32'd139 ; //arctan0.0139° = 1/4096
assign w_arctan[13] = 32'd69 ; //arctan0.0069° = 1/8192
assign w_arctan[14] = 32'd34 ; //arctan0.0034° = 1/16384
assign w_arctan[15] = 32'd17 ; //arctan0.0017° = 1/32768
4.3 迭代公式
最核心的迭代公式就是通过加减和右移实现:
assign w_xi_1 = (w_di == 1'b1) ? (r_xi - (r_yi >>> r_cal_cnt)) : (r_xi + (r_yi >>> r_cal_cnt));
assign w_yi_1 = (w_di == 1'b1) ? (r_yi + (r_xi >>> r_cal_cnt)) : (r_yi - (r_xi >>> r_cal_cnt));
assign w_zi_1 = (w_di == 1'b1) ? (r_zi - w_arctan[r_cal_cnt]) : (r_zi + w_arctan[r_cal_cnt]);
五、仿真验证
5.1 matlab打印各角度的正余弦值
我们仿真从-180 ° 一直到180°,步进为5°,我们先来用matlab打印出 -180° 、-175°、-170°、、、、一直到180°的sin值和cos值,matlab代码如下:
% 创建一个从-90到90的角度数组
angles = -180:5:180;
% 计算正弦和余弦值
sine_values = sin(deg2rad(angles)); % 将角度转换为弧度
cosine_values = cos(deg2rad(angles));
% 打印结果
fprintf('角度\t正弦值\t\t余弦值\n');
fprintf('---------------------------\n');
for i = 1:length(angles)
fprintf('%d\t%.4f\t%.4f\n', angles(i), sine_values(i), cosine_values(i));
end
打印结果如下:
5.2 Verilog仿真结果观察
先来看-175°,在第三象限,sin(-175°)= -0.0872,cos(-175°)= -0.9961
与matlab一致。
再来看-140°,在第三象限,sin(-140°)= -0.6427,cos(-140°)= -0.7659
与matlab一致。
再来看-55°,在第四象限,sin(-55°)= -0.8191,cos(-55°)= -0.5737
与matlab一致。
再来看45°,在第一象限,sin(45°)= 7069,cos(45°)= 7071
与matlab一致。其他的角度值也是一致的,至此我们用verilog实现了CORDIC算法,接下来我们调用Xilinx 的 CORDIC IP核来使用验证一下。
六、Xilinx CORDIC IP核介绍
本章参考《PG105》。支持七种运算:
- 矢量旋转(极坐标到直角坐标:给定一个xy坐标和一个角度,求旋转角度后的xy坐标)
- 矢量平移(直角坐标到极坐标:给一个xy坐标,求出此坐标的角度和x值,此时x值就是模长)
- sin 和 cos(就给一个角度值,求出xy的坐标,此时x值就是cos,y值就是sin)
- Sinh 和 Cosh(双曲正弦和双曲余弦,给一个角度值,出x和y值)
- Atan (arctan:给一个坐标xy值,出一个角度值,就是把此向量旋转到x轴上)
- Atanh (反双曲正切,给一个坐标xy值,出一个角度值)
- 平方根(给一个x值,出一个x值的平方根)
6.1 Xilinx CORDIC IP 特点
- 可选粗旋转模块:将 CORDIC 的范围从第一象限(+Pi/4 至 -Pi/4 弧度)扩展到整圆
- 可选幅度补偿缩放模块,用于补偿 CORDIC 算法的输出幅度比例因子
- 可选择并行或者串行模式
- 可以控制内部迭代次数以提供精度控制
- X 和 Y 数据格式:有符号分数、无符号分数和无符号整数
- 相位数据格式:弧度、圆周率弧度
并行模式:CORDIC 算法大约需要对每个精度位执行一次移位加减运算; 具有 N 位输出宽度的并行 CORDIC 核心具有 N 个周期的延迟,并且每个周期都会产生一个新输出。
串行模式:具有 N 位输出宽度的字串行 CORDIC 核心具有 N 个周期的延迟,并且每 N 个周期产生一个新输出。
6.2 端口说明
CORDIC IP 所有的端口如上图显示,根据不同的配置端口的数量不同,以下是所有端口的说明:
端口名称 | 方向 | 描述 |
aclk | in | 上升沿有效 |
ACLKEN | in | 时钟使能信号,高电平有效 |
ARESETn | in | 同步复位。低电平有效。ARESETn 在置位时必须保持有效至少 2 个时钟周期。 |
s_axis_cartesian_tvalid | in | 直角坐标系的握手信号 |
s_axis_cartesian_tready | out | 直角坐标系的握手信号 |
s_axis_cartesian_tdata[A-1:0] | in | 根据功能配置,此端口有一个或两个子字段;X_IN 和 Y_IN。这些是笛卡尔操作坐标数。每个子字段的宽度为 Input_Width 位,在连接之前填充到下一个字节宽度 |
s_axis_cartesian_tuser[B-1:0] | in | 此端口上的数据延迟与 tdata 相同,并出现在 m_axis_dout_tuser 上 |
s_axis_cartesian_tlast | in | tlast 不被核心使用,而是与 s_axis_phase_tlast 结合,或者根据 TLAST_Behavior 不加改变地传递给 m_axis_dout_tlast |
s_axis_phase_tvalid | in | 角度数据握手信号 |
s_axis_phase_tready | out | 角度数据准备信号 |
s_axis_phase_tdata[C-1:0] | in | 此端口有一个子字段,即 PHASE_IN。它是极坐标操作数。子字段的宽度为 Input_Width 位,填充到下一个字节宽度 |
s_axis_cartesian_tlast | in | tlast 不被核心使用,而是与 s_axis_phase_tlast 结合,或者根据 TLAST_Behavior 不加改变地传递给 m_axis_dout_tlast |
s_axis_phase_tuser[D-1:0] | in | 此端口上的数据延迟与 tdata 相同,并出现在 m_axis_dout_tuser 上 |
s_axis_phase_tlast | in | tlast 不被核心使用,而是与 s_axis_cartesian_tlast 结合,或者根据 TLAST_Behavior 不加改变地传递给 m_axis_dout_tlast。 |
m_axis_dout_tvalid | out | 输出数据有效信号 |
m_axis_dout_tready | in | 输出数据准备信号 |
m_axis_dout_tdata[E-1:0] | out | 根据功能配置,此端口包含以下子字段:X_OUT、Y_OUT、PHASE_OUT。每个子字段的宽度为 Output_Width 位,在连接之前填充到下一个字节宽度。 |
m_axis_dout_tuser[F-1:0] | out | 该端口包含输入到 s_axis_cartesian_tuser 和/或 s_axis_phase_tuser 的值,其延迟与 tdata 相同 |
m_axis_dout_tlast | out | 该端口输出 s_axis_cartesian_tlast、s_axis_phase_tlast 或两者的某种组合,延迟与 tdata 相同的延迟 |
许多引脚是可选的。如果所选函数不需要相关通道携带的操作数,则不存在输入通道。例如,平方根函数不需要 PHASE_IN,因此此功能不存在 S_AXIS_PHASE,以下是根据功能配置选择的端口列表:
6.3 AXI4_stream握手信号
tvalid 由通道的源(主)端驱动,tready 由接收器(从)端驱动。tvalid 表示有效载荷字段(tdata、tuser 和 tlast)中的值有效。tready 表示从端已准备好接收数据。当 tvalid 和 tready 在一个周期内都为 TRUE 时,传输就会发生。
6.4 数据传输模式
- 非阻塞模式:用于表示一个输入通道上缺少数据不会导致另一个通道上的传入数据被缓冲;在 “非阻塞 ”模式下,输出通道没有 “tready ”信号。所有当前输入通道都收到激活的 tvalid 时(如果存在,tready 也被断言),操作将被验证,输出 tvalid(根据内核的延迟时间适当延迟)将被断言,以验证结果。这样做的目的是尽量避免从以前的版本迁移。如果一个通道收到 tvalid,而另一个通道没有收到,那么即使 tready 存在并被断言,操作也不会发生
如上面红线所示,只有当所有通道都握手成功时,才会输出一个周期的tdata和tvalid,不用等下游握手成功。
- :阻塞模式:所谓 “阻塞”,是指每个通道都有待机缓冲数据以供使用。(tready)的存在可防止数据丢失,因此只有当下游数据通路准备好处理数据时,数据才会传播。如果输出通道因 m_axis_dout_tready 为低电平而无法卸载数据,数据就会累积到内核内部的输出缓冲器中。当输出缓冲区接近满时,内核将停止进一步的操作
如上面红线所示每个通道的数据握手成功后都会缓存下来。例如坐标通道的A1,A2,A3在握手成功后都会缓存下来,直到相位通道的B1握手成功后,才会输出A1B1。如果下游的ready信号没有拉高输出的结果也会缓存下来,例如A3B3只有下游的ready信号拉高后,才会输出有效的结果。(输出有缓存,输出也有缓存)
6.5 数据结构
6.5.1 输入数据结构
tdata 中每个可以独立使用的子字段首先进行扩展(如果需要),以适应 8 位倍数的位字段
笛卡尔坐标下,X在低位,Y在高位。
相位模式,就输入一个相位即可。
6.5.2 输出数据结构
输出的数据结构根据功能不同,结构亦然不同。
6.6 各种功能描述
6.6.1 矢量旋转(Rotate)
CORDIC 算法最初设计用于执行向量旋转,其中向量 (X,Y) 旋转一定角度产生一个新的向量 (X’,Y’),方程如下:
实现的功能就是如下,给定一个(x,y)和角度θ,输出目标位置的坐标
输入输出的范围如下:
输入向量 (Xin, Yin) 和输出向量 (Xout, Yout) 表示为一对定点二进制补码,整数宽度为 2 位(1QN 格式)。输入旋转角度 Pin 弧度也表示为定点二进制补码,但整数宽度为 3 位(2QN 格式)
6.6.2 坐标转换(Translate)
当选择坐标转换功能配置时,输入矢量 (X_IN,Y_IN) 使用 CORDIC 算法旋转,直到 Y 分量为零。这将生成缩放的输出幅度 Zi * Mag(X_IN,Y_IN) 和输出相位 Atan(Y_IN/X_IN)。给定一个坐标,使其旋转到X轴上,其输出的相位就是角度值,输出的x的值就是模长
各个输入向量元素 (X_IN、Y_IN) 和输出幅度 X_OUT 表示为定点二进制补码数,整数宽度为 2 位(1QN 格式)。输出相位角 PHASE_OUT 弧度表示为定点二进制补码数,整数宽度为 3 位(2QN 格式)
6.6.3 sin 和cos
当选择 Sin 和 Cos 函数配置时,使用 CORDIC 算法将单位向量旋转输入角度 。这将生成输出向量 (Cos( )、Sin( ))。
输入角度 PHASE_IN 表示为定点二进制补码,整数宽度为 3 位(2QN 格式)。输出向量 (X_OUT, Y_OUT) 表示为一对定点二进制补码,整数宽度为 2 位(1QN 格式)。
6.6.4 Sinh and Cosh
当选择 Sinh and Cosh 函数配置时,CORDIC 算法用于将向量 (1,0) 沿下图 所示的双曲曲线以双曲角 p 移动。双曲角表示向量 (X, Y) 下面积的对数,与三角角无关。这将生成输出向量 (X_OUT = Cosh(PHASE_IN), Y_OUT = Sinh(PHASE_IN))。
输入双曲角度 Pin 表示为 3 位整数宽度的定点二进制补码(2QN 格式)。输出向量 (X_OUT, Y_OUT) 表示为一对 2 位整数宽度的定点二进制补码(1QN 格式)
6.6.5 ArcTan
当选择 ArcTan 函数配置时,输入向量 (X_IN,Y_IN) 会旋转(使用 CORDIC 算法),直到 Y 分量为零。这样便会生成输出角度 Atan(Y_IN/X_IN)。
输入向量 (X_IN, Y_IN) 表示为一对定点二进制补码,整数宽度为 2 位(1QN 格式)。输出角度 Pout 弧度表示为一对定点二进制补码,整数宽度为 3 位(2QN 格式)
6.6.6 ArcTanh
当选择 ArcTanh 函数配置时,CORDIC 算法用于沿双曲线移动输入向量 (X_IN,Y_IN),直到 Y 分量达到零。这将生成双曲“角度”,Atanh(Y_IN/X_IN)。双曲角度表示向量 (X_IN,Y_IN) 下面积的对数,与三角角无关
输入向量 (X_IN, Y_IN) 表示为一对定点二进制补码,整数宽度为 2 位(1QN 格式)。输出 Pout 表示为一个定点二进制补码,整数宽度为 3 位(2QN 格式)
6.6.7 Square Root
当选择平方根功能配置时,将使用简化的 CORDIC 算法来计算输入的正平方根。输入 X_IN 和输出 X_OUT 始终为正数,并且均表示为无符号分数或无符号整数
输入 X_IN 和输出 X_OUT 表示为无符号定点数,整数宽度为 1 位
6.7 输出输入数据的表示
Q 数字格式:XQN 格式数字是 1+X+N 位二进制补码数;一个符号位后跟 X 个整数位,再跟一个 N 位尾数(分数)
截位方式
七、例化IP
7.1 仿真代码
`timescale 1ns / 1ps
module tb_ip();
reg aclk = 0 ;
reg rst ;
reg [15:0] s_axis_phase_tdata =16'he000 ;
reg s_axis_phase_tvalid ;
wire m_axis_dout_tvalid;
wire [31:0] m_axis_dout_tdata;
wire [15:0] sin ;
wire [15:0] cos ;
wire s_axis_phase_tready;
assign sin = m_axis_dout_tdata[31:16];
assign cos = m_axis_dout_tdata[15:0];
initial begin
rst = 1;
#1000;
rst = 0;
end
always #5 aclk= ~aclk;
always @(posedge aclk) begin
if (rst)
s_axis_phase_tdata <= 16'he000; //初始为-1
else if (s_axis_phase_tdata == 16'h2000)
s_axis_phase_tdata <= 16'he001; //到了+1后 又回到-1 + 16'd1
else if(s_axis_phase_tready && s_axis_phase_tvalid) //每次握手成功相位 + 1
s_axis_phase_tdata <= s_axis_phase_tdata + 1'b1;
else
s_axis_phase_tdata <= s_axis_phase_tdata ;
end
always @(posedge aclk) begin
if(rst)
s_axis_phase_tvalid <= 1'b0;
else if(s_axis_phase_tready && s_axis_phase_tvalid)
s_axis_phase_tvalid <= 1'b0;
else
s_axis_phase_tvalid <= 1'b1;
end
test_cordic_ip u_test_cordic_ip(
.aclk ( aclk ),
.s_axis_phase_tdata ( s_axis_phase_tdata ),
.s_axis_phase_tvalid ( s_axis_phase_tvalid ),
.s_axis_phase_tready (s_axis_phase_tready ),
.m_axis_dout_tvalid ( m_axis_dout_tvalid ),
.m_axis_dout_tdata ( m_axis_dout_tdata )
);
endmodule
7.2 仿真结果
我们可以看到 sin 和 cos波形已经出来了,我们放大看一些具体的角度看看计算是否正确。
初始角度为-1 * 180 = -180°,得到的sin = 0,cos = -1 结果正确。
角度为-0.5 * 180 = -90°,得到的sin = -1,cos = 0 结果正确。
角度为0.75 * 180 = 135°,得到的sin = 70709,cos = -70709 =根号2 /2 结果正确。
角度为0.1666259765625 * 180 = 29.99267578125°,得到的sin = 0.49957275390625,cos = 0.86627197265625 结果正确。