硬件:ZYNQ7010
软件:MATLAB 2019b、Vivado 2017.4、HLS 2017.4、System Generator 2017.4
1、CORDIC算法计算正余弦
CORDIC算法详细分析网上有很多资料,它的主要思想是用一系列旋转去逼近目标角度,这一系列旋转的角度为 θ = a r c t a n ( 2 − i ) \theta=arctan(2^{-i}) θ=arctan(2−i), i i i 是迭代次数。下面给出了用CORDIC算法计算正余弦的代码,其中 s1 不做任何优化,数据类型都用的 float 型;s2 对数据类型做了定点数优化;s3 在 s2 的基础上,对迭代的循环做了流水线优化。
//cordic.h
#ifndef _CORDIC_H_
#define _CORDIC_H_
#include <ap_int.h>
#include <ap_fixed.h>
#define NUM_ITERATIONS 9
//#define s1
//#define s2
#define s3
#if defined s1
typedef float THETA_TYPE;
typedef float COS_SIN_TYPE;
#endif
#if defined s2 || defined s3
typedef ap_fixed<16,8> THETA_TYPE;
typedef ap_fixed<16,2> COS_SIN_TYPE;
#endif
void cordic(THETA_TYPE theta, COS_SIN_TYPE &s, COS_SIN_TYPE &c);
#endif
//cordic.cpp
#include "cordic.h"
const THETA_TYPE cordic_phase[NUM_ITERATIONS] = {
45, 26.565, 14.036, 7.125, 3.576, 1.790, 0.895, 0.448, 0.224
};
#if defined s1
void cordic(THETA_TYPE theta, COS_SIN_TYPE &s, COS_SIN_TYPE &c)
{
COS_SIN_TYPE current_cos = 0.607255; // 6, 1/1.64669
COS_SIN_TYPE current_sin = 0;
COS_SIN_TYPE factor = 1.0;
for(int i=0; i<NUM_ITERATIONS; i++)
{
ap_int<2> sigma = (theta < 0)?-1:1;
COS_SIN_TYPE temp_cos = current_cos;
current_cos = current_cos-current_sin*sigma*factor;
current_sin = temp_cos*sigma*factor+current_sin;
theta = theta-sigma*cordic_phase[i];
factor = factor/2.0;
}
s = current_sin;
c = current_cos;
}
#endif
#if defined s2 || defined s3
void cordic(THETA_TYPE theta, COS_SIN_TYPE &s, COS_SIN_TYPE &c)
{
COS_SIN_TYPE current_cos = 0.607255; // 6, 1/1.64669
COS_SIN_TYPE current_sin = 0;
COS_SIN_TYPE factor = 1.0;
ITERATIONS_LOOP:
for(int i=0; i<NUM_ITERATIONS; i++)
{
ap_int<2> sigma = (theta < 0)?-1:1;
COS_SIN_TYPE temp_cos = current_cos;
current_cos = current_cos-current_sin*sigma*factor;
current_sin = temp_cos*sigma*factor+current_sin;
theta = theta-sigma*cordic_phase[i];
factor >>= 1;
}
s = current_sin;
c = current_cos;
}
#endif
三个 solution 的资源使用量和计算性能如下图所示。
2、上板验证
把 s3 的模块端口设置成 ap_ctrl_none, 重新综合,导出 IP 核。在FPGA的顶层文件里例化 cordic IP 核和一个 ila IP 核,让 cordic 计算 30° 和 60° 角的正余弦值。
module cordic_test_top(
input resetn,
input clk
);
wire [15:0] w_theta;
reg [15:0] r_theta;
reg [31:0] cnt;
always @(posedge clk or negedge resetn) begin
if(!resetn) begin
cnt <= 32'd0;
end
else begin
if(cnt == 32'd1000) cnt <= 32'd0;
else cnt <= cnt+1'd1;
end
end
always @(posedge clk or negedge resetn) begin
if(!resetn) begin
r_theta <= 16'd0;
end
else begin
if(cnt == 32'd500) r_theta <= {8'd30, 8'd0};
else if(cnt == 32'd1000) r_theta <= {8'd60, 8'd0};
else r_theta <= r_theta;
end
end
assign w_theta = r_theta;
wire [15:0] s, c;
wire s_V_ap_vld, c_V_ap_vld;
reg [15:0] r_s, r_c;
cordic_0 cordic_inst (
.s_V_ap_vld(s_V_ap_vld), // output wire s_V_ap_vld
.c_V_ap_vld(c_V_ap_vld), // output wire c_V_ap_vld
.ap_clk(clk), // input wire ap_clk
.ap_rst(~resetn), // input wire ap_rst
.theta_V(w_theta), // input wire [15 : 0] theta_V
.s_V(s), // output wire [15 : 0] s_V
.c_V(c) // output wire [15 : 0] c_V
);
always @(posedge clk or negedge resetn) begin
if(!resetn) begin
r_s <= 16'd0;
end
else begin
if(s_V_ap_vld) begin
r_s <= s;
end
end
end
always @(posedge clk or negedge resetn) begin
if(!resetn) begin
r_c <= 16'd0;
end
else begin
if(c_V_ap_vld) begin
r_c <= c;
end
end
end
ila_0 ila0 (
.clk(clk), // input wire clk
.probe0(w_theta), // input wire [15:0] probe0
.probe1(r_s), // input wire [15:0] probe1
.probe2(s_V_ap_vld), // input wire [0:0] probe2
.probe3(r_c), // input wire [15:0] probe3
.probe4(c_V_ap_vld) // input wire [0:0] probe4
);
ila 上看到的波形如下图所示。注意要正确设置观测量的数据类型,即定点数和小数点位置。从图中可以看出计算的角度比较准确。