基于cordic ip核实现arctan@TOC
最近在研究相位补偿电路,需要用到反正切函数求解相位差,因此学习了该ip核的使用,写一篇小记录,希望能帮到有需要的盆友。
首先将functional selection选为arc tan,随后设置输入位宽和输出位宽。
需要注意的是,输入数据默认格式为“有2bit整数位的有符号定点小数”,输出数据默认格式为“有3bit整数位的有符号定点小数”,依据需要制定位宽即可。
图中设置的input width为16bit,即cos和sin值都用16bit来表示,因此可以看到数据端口的输入数据总位宽为32bit。在实际写入数据时,将sin值写在[31:16]处,将cos值写在[15:0]处。
需要注意的是,输入数据的总位宽一定为8的倍数。例如如果input width都为10bit,则有效数据为20bit,实际数据端口位宽为32bit。在实际写入数据时,需要将sin值写在[25:16]处,将cos值写在[9:0]处,[15:10]和[31:26]处补0即可。
为了验证该ip核的功能,写了一个.v文件,依次写入30°、45°、60°、60°、45°、30°、-30°、-45°、-60°、-60°、-45°、-30°… 的cos值和sin值,求取反正切的结果。
代码如下,较为基础,不做解释。
// A code block
module test_arctan(
input clk,
input rst_n,
output reg [15:0] arctan_out_tdata,
output wire arctan_out_tvalid
);
reg [2:0] state;
reg [2:0] next_state;
reg [4:0] cnt;
reg [15:0] cos,sin;
reg [31:0] arctan_in_tdata;
reg arctan_in_tvalid;
wire[15:0] arctan_dout;
localparam S_IDLE =3'b001;
localparam S_START =3'b101;
localparam S_CPT =3'b100;
localparam S_STOP =3'b110;
always@(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
state<=S_IDLE;
else
state<=next_state;
end
always@(*)
begin
case(state)
S_IDLE:
next_state<=S_START;
S_START:
next_state<=S_CPT;
S_CPT:
if(arctan_out_tvalid)
next_state<=S_STOP;
else
next_state<=S_CPT;
S_STOP:
if(cnt<5'd23)
next_state<=S_START;
else
next_state<=S_STOP;
default:
next_state<=state;
endcase
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
cnt<=0;
else if(state==S_STOP && next_state!=state)
cnt<=cnt+1;
else
cnt<=cnt;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
arctan_in_tvalid<=0;
else if(state==S_START)
arctan_in_tvalid<=1;
else
arctan_in_tvalid<=0;
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
arctan_in_tdata<=0;
else if(state==S_START)
arctan_in_tdata<={sin,cos};
end
always@(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
arctan_out_tdata<=0;
else if(next_state==S_STOP)
begin
if(cos[15]==1'b0)
arctan_out_tdata<=arctan_dout;
else
arctan_out_tdata<=arctan_dout-16'b0110_0100_1000_0111;
end
else
arctan_out_tdata<=arctan_out_tdata;
end
always@(*)
begin
case(cnt)
5'd0: begin //30°
cos<=16'b0011_0111_0110_1100;//根号3/2
sin<=16'b0010_0000_0000_0000;//1/2
end
5'd1: begin //45°
cos<=16'b0010_1101_0100_0001;
sin<=16'b0010_1101_0100_0001;
end
5'd2: begin //60°
cos<=16'b0010_0000_0000_0000;
sin<=16'b0011_0111_0110_1100;
end
5'd3: begin //60°
cos<=16'b0010_0000_0000_0000;
sin<=16'b0011_0111_0110_1100;
end
5'd4: begin //45°
cos<=16'b0010_1101_0100_0001;
sin<=16'b0010_1101_0100_0001;
end
5'd5: begin //30°
cos<=16'b0011_0111_0110_1100;//根号3/2
sin<=16'b0010_0000_0000_0000;//1/2
end
5'd6: begin //-30°
cos<=16'b1100_1000_1001_0100;
sin<=16'b0010_0000_0000_0000;
end
5'd7: begin //-45°
cos<=16'b1101_0010_1011_1111;
sin<=16'b0010_1101_0100_0001;
end
5'd8: begin //-60°
cos<=16'b1110_0000_0000_0000;
sin<=16'b0011_0111_0110_1100;
end
5'd9: begin //-60°
cos<=16'b1110_0000_0000_0000;
sin<=16'b0011_0111_0110_1100;
end
5'd10: begin //-45°
cos<=16'b1101_0010_1011_1111;
sin<=16'b0010_1101_0100_0001;
end
5'd11: begin //-30°
cos<=16'b1100_1000_1001_0100;
sin<=16'b0010_0000_0000_0000;
end
5'd12: begin //30°
cos<=16'b0011_0111_0110_1100;//根号3/2
sin<=16'b0010_0000_0000_0000;//1/2
end
5'd13: begin //45°
cos<=16'b0010_1101_0100_0001;
sin<=16'b0010_1101_0100_0001;
end
5'd14: begin //60°
cos<=16'b0010_0000_0000_0000;
sin<=16'b0011_0111_0110_1100;
end
5'd15: begin //60°
cos<=16'b0010_0000_0000_0000;
sin<=16'b0011_0111_0110_1100;
end
5'd16: begin //45°
cos<=16'b0010_1101_0100_0001;
sin<=16'b0010_1101_0100_0001;
end
5'd17: begin //30°
cos<=16'b0011_0111_0110_1100;//根号3/2
sin<=16'b0010_0000_0000_0000;//1/2
end
5'd18: begin //-30°
cos<=16'b1100_1000_1001_0100;
sin<=16'b0010_0000_0000_0000;
end
5'd19: begin //-45°
cos<=16'b1101_0010_1011_1111;
sin<=16'b0010_1101_0100_0001;
end
5'd20: begin //-60°
cos<=16'b1110_0000_0000_0000;
sin<=16'b0011_0111_0110_1100;
end
5'd21: begin //-60°
cos<=16'b1110_0000_0000_0000;
sin<=16'b0011_0111_0110_1100;
end
5'd22: begin //-45°
cos<=16'b1101_0010_1011_1111;
sin<=16'b0010_1101_0100_0001;
end
5'd23: begin //-30°
cos<=16'b1100_1000_1001_0100;
sin<=16'b0010_0000_0000_0000;
end
default:
begin
cos<=16'b0000_0000_0000_0000;
sin<=16'b0000_0000_0000_0000;
end
endcase
end
cordic_1 arctan (
.aclk(clk), // input wire aclk
.s_axis_cartesian_tvalid(arctan_in_tvalid), // input wire s_axis_cartesian_tvalid
.s_axis_cartesian_tdata(arctan_in_tdata), // input wire [31 : 0] s_axis_cartesian_tdata
.m_axis_dout_tvalid(arctan_out_tvalid), // output wire m_axis_dout_tvalid
.m_axis_dout_tdata(arctan_dout) // output wire [31 : 0] m_axis_dout_tdata
);
endmodule
调试过程中有遇到一个问题。
为了使第三种情况输出结果也为-pi/6,查了很多资料,最后在xilinx社区找到了解决办法。
因此,在代码中加了一段对cos值符号位的判断,由此来决定是否对输出结果进行“减去2pi”的操作。
always@(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
arctan_out_tdata<=0;
else if(next_state==S_STOP)
begin
if(cos[15]==1'b0)
arctan_out_tdata<=arctan_dout;
else
arctan_out_tdata<=arctan_dout-16'b0110_0100_1000_0111;
end
else
arctan_out_tdata<=arctan_out_tdata;
end
最终输出正常,仿真结果如下。(图中为输入输出均为32bit的情况)
xilinx社区的完整问题描述和解决方案: link.