個人的にCORDICによる三角関数の実装に興味があったので実装しました。
次の記事 https://qiita.com/cyebu1103/items/2d51212f27788f8b3c8f がとても親切に解説してくれています。
演算子を極力使わないように実装を試みました。非パイプラインです。
数値はQ29表記の32ビット符号付き固定小数点、入力レンジは[-6.28,6.28]です。
Xilinx LogiCoreIPの三角関数IPの入力範囲は確か[-3.28,3.28]。
ALTERA Megafunctionのalt_sincosのように入力制限がないものを作りたい。
上手なバレルシフタの記述方法を検討中。
COREDIC_sincos.vhd
module CORDIC_sincos(input wire clk, input wire st, input wire [31:0] theta, output wire [31:0] sin, output wire [31:0] cos);
//Format: S2Q29, 32 bit fixed point.
reg on; initial on=1'b0;
reg [4:0] cnt;
always@(clk) on <= (on==1'b0)? ((st==1'b1)? 1'b1:1'b0) : ((cnt==5'd24)? 1'b0:1'b1);
localparam signed [31:0] PI = 32'h6487ed51; //3.141593e+00 (Tranceted error: 1.215419e-10)
localparam signed [31:0] PI_2 = 32'h3243f6a8; //1.570796e+00 (Tranceted error: 9.920935e-10)
localparam signed [31:0] inv_PI = 32'h0a2f9836; //3.183099e-01 (Tranceted error: 1.665406e-09)
localparam signed [31:0] inv_2PI = 32'h0517cc1b; //1.591549e-01 (Tranceted error: 8.327029e-10)
localparam signed [31:0] inv_sqrt2 = 32'h16a09e66; //7.071068e-01 (Tranceted error: 9.257461e-10)
localparam signed [31:0] PI_4 = 32'h1921fb54; //7.853982e-01 (Tranceted error: 4.960468e-10)
localparam signed [31:0] PI3_4 = 32'h4b65f1fc; //2.356194e+00 (Tranceted error: 1.488140e-09)
localparam signed [31:0] inv_k = 32'h136e9db5; //6.072529e-01 (Tranceted error: 6.127276e-11)
localparam signed [31:0] atan_no0_P = 32'h1921fb54; //7.853982e-01 (Tranceted error: 4.960468e-10)
localparam signed [31:0] atan_no1_P = 32'h0ed63382; //4.636476e-01 (Tranceted error: 1.286868e-09)
localparam signed [31:0] atan_no2_P = 32'h07d6dd7e; //2.449787e-01 (Tranceted error: 5.466124e-10)
localparam signed [31:0] atan_no3_P = 32'h03fab753; //1.243550e-01 (Tranceted error: 6.222629e-10)
localparam signed [31:0] atan_no4_P = 32'h01ff55bb; //6.241881e-02 (Tranceted error: 8.353672e-10)
localparam signed [31:0] atan_no5_P = 32'h00ffeaad; //3.123983e-02 (Tranceted error: 1.610138e-09)
localparam signed [31:0] atan_no6_P = 32'h007ffd55; //1.562373e-02 (Tranceted error: 8.071138e-10)
localparam signed [31:0] atan_no7_P = 32'h003fffaa; //7.812341e-03 (Tranceted error: 1.247584e-09)
localparam signed [31:0] atan_no8_P = 32'h001ffff5; //3.906230e-03 (Tranceted error: 6.210636e-10)
localparam signed [31:0] atan_no9_P = 32'h000ffffe; //1.953123e-03 (Tranceted error: 1.241769e-09)
localparam signed [31:0] atan_no10_P = 32'h0007ffff; //9.765622e-04 (Tranceted error: 1.552204e-09)
localparam signed [31:0] atan_no11_P = 32'h0003ffff; //4.882812e-04 (Tranceted error: 1.823840e-09)
localparam signed [31:0] atan_no12_P = 32'h0001ffff; //2.441406e-04 (Tranceted error: 1.857795e-09)
localparam signed [31:0] atan_no13_P = 32'h0000ffff; //1.220703e-04 (Tranceted error: 1.862039e-09)
localparam signed [31:0] atan_no14_P = 32'h00007fff; //6.103516e-05 (Tranceted error: 1.862569e-09)
localparam signed [31:0] atan_no15_P = 32'h00003fff; //3.051758e-05 (Tranceted error: 1.862636e-09)
localparam signed [31:0] atan_no16_P = 32'h00001fff; //1.525879e-05 (Tranceted error: 1.862644e-09)
localparam signed [31:0] atan_no17_P = 32'h00000fff; //7.629395e-06 (Tranceted error: 1.862645e-09)
localparam signed [31:0] atan_no18_P = 32'h000007ff; //3.814697e-06 (Tranceted error: 1.862645e-09)
localparam signed [31:0] atan_no0_N = 32'he6de04ab; //-7.853982e-01 (Tranceted error: 1.366598e-09)
localparam signed [31:0] atan_no1_N = 32'hf129cc7d; //-4.636476e-01 (Tranceted error: 5.757768e-10)
localparam signed [31:0] atan_no2_N = 32'hf8292281; //-2.449787e-01 (Tranceted error: 1.316033e-09)
localparam signed [31:0] atan_no3_N = 32'hfc0548ac; //-1.243550e-01 (Tranceted error: 1.240382e-09)
localparam signed [31:0] atan_no4_N = 32'hfe00aa44; //-6.241881e-02 (Tranceted error: 1.027278e-09)
localparam signed [31:0] atan_no5_N = 32'hff001552; //-3.123983e-02 (Tranceted error: 2.525072e-10)
localparam signed [31:0] atan_no6_N = 32'hff8002aa; //-1.562373e-02 (Tranceted error: 1.055531e-09)
localparam signed [31:0] atan_no7_N = 32'hffc00055; //-7.812341e-03 (Tranceted error: 6.150612e-10)
localparam signed [31:0] atan_no8_N = 32'hffe0000a; //-3.906230e-03 (Tranceted error: 1.241582e-09)
localparam signed [31:0] atan_no9_N = 32'hfff00001; //-1.953123e-03 (Tranceted error: 6.208760e-10)
localparam signed [31:0] atan_no10_N = 32'hfff80000; //-9.765622e-04 (Tranceted error: 3.104407e-10)
localparam signed [31:0] atan_no11_N = 32'hfffc0000; //-4.882812e-04 (Tranceted error: 3.880510e-11)
localparam signed [31:0] atan_no12_N = 32'hfffe0000; //-2.441406e-04 (Tranceted error: 4.850638e-12)
localparam signed [31:0] atan_no13_N = 32'hffff0000; //-1.220703e-04 (Tranceted error: 6.063298e-13)
localparam signed [31:0] atan_no14_N = 32'hffff8000; //-6.103516e-05 (Tranceted error: 7.579123e-14)
localparam signed [31:0] atan_no15_N = 32'hffffc000; //-3.051758e-05 (Tranceted error: 9.473904e-15)
localparam signed [31:0] atan_no16_N = 32'hffffe000; //-1.525879e-05 (Tranceted error: 1.184238e-15)
localparam signed [31:0] atan_no17_N = 32'hfffff000; //-7.629395e-06 (Tranceted error: 1.480300e-16)
localparam signed [31:0] atan_no18_N = 32'hfffff800; //-3.814697e-06 (Tranceted error: 1.850386e-17)
reg sign;
reg zero;
reg reverse;
reg angle_zero;
reg signed [31:0] x;
reg signed [31:0] y;
reg signed [31:0] x_sh;
reg signed [31:0] y_sh;
reg quadrant;
reg [31:0] theta_sh;
reg [31:0] theta_sh_buf;
reg [31:0] angle_tb;
reg [31:0] sin_in;
reg [31:0] cos_in;
reg div;
assign sin=sin_in;
assign cos=cos_in;
always@(posedge clk)
begin
div=(on==1'b1)? ~div:1'b0;
if(div==1'b0)
begin
cnt <= (on==1'b1)? cnt+1'b1: 1'b0;
if(cnt==5'd1) angle_zero <= (theta==1'b0)? 1'b1:1'b0;
if(cnt==5'd1) theta_sh <= (theta[31]==1'b0)? theta-PI:theta+PI; // Range convert [-2pi,2pi]->[-pi,pi]
else theta_sh <= (theta_sh_buf!=1'b0)? theta_sh_buf+angle_tb:1'b0;
if(cnt==5'd2) reverse <= (theta_sh[31]==1'b1)? 1'b1:1'b0; else reverse<=reverse;
sign <= theta_sh[31];
if(cnt==5'd6)
begin
x<=(angle_zero!=1'b1)? inv_k:32'h20000000;
y<=(angle_zero!=1'b1)? inv_k:1'b0;
end
else
begin
x <= (zero!=1'b1)? ((sign==1'b0)? x-y_sh: x+y_sh):x;
y <= (zero!=1'b1)? ((sign==1'b0)? y+x_sh: y-x_sh):y;
end
if(cnt==5'd23)
begin
case ({reverse,quadrant})
2'b00: begin cos_in<=~x+1'b1; sin_in<=~y+1'b1; end
2'b01: begin cos_in<=y; sin_in<=~x+1'b1; end
2'b10: begin cos_in<=x; sin_in<=y; end
2'b11: begin cos_in<=~y+1'b1; sin_in<=x; end
default: begin cos_in<=1'b0; sin_in<=1'b0; end
endcase
end
else
begin
cos_in<=cos_in;
sin_in<=sin_in;
end
end
else
begin
case(cnt)
5'd2: angle_tb <= (theta_sh[31]==1'b1)? PI:1'b0; //Range convert [-pi,pi] to [0,pi];
5'd4: angle_tb <= (quadrant==1'b1)? -PI_2:1'b0; //Range convert [0,pi] to [0,pi/2];
5'd5: angle_tb <= (theta_sh[31]==1'b0)? atan_no0_N:atan_no0_P;
5'd6: angle_tb <= (theta_sh[31]==1'b0)? atan_no1_N:atan_no1_P;
5'd7: angle_tb <= (theta_sh[31]==1'b0)? atan_no2_N:atan_no2_P;
5'd8: angle_tb <= (theta_sh[31]==1'b0)? atan_no3_N:atan_no3_P;
5'd9: angle_tb <= (theta_sh[31]==1'b0)? atan_no4_N:atan_no4_P;
5'd10: angle_tb <= (theta_sh[31]==1'b0)? atan_no5_N:atan_no5_P;
5'd11: angle_tb <= (theta_sh[31]==1'b0)? atan_no6_N:atan_no6_P;
5'd12: angle_tb <= (theta_sh[31]==1'b0)? atan_no7_N:atan_no7_P;
5'd13: angle_tb <= (theta_sh[31]==1'b0)? atan_no8_N:atan_no8_P;
5'd14: angle_tb <= (theta_sh[31]==1'b0)? atan_no9_N:atan_no9_P;
5'd15: angle_tb <= (theta_sh[31]==1'b0)? atan_no10_N:atan_no10_P;
5'd16: angle_tb <= (theta_sh[31]==1'b0)? atan_no11_N:atan_no11_P;
5'd17: angle_tb <= (theta_sh[31]==1'b0)? atan_no12_N:atan_no12_P;
5'd18: angle_tb <= (theta_sh[31]==1'b0)? atan_no13_N:atan_no13_P;
5'd19: angle_tb <= (theta_sh[31]==1'b0)? atan_no14_N:atan_no14_P;
5'd20: angle_tb <= (theta_sh[31]==1'b0)? atan_no15_N:atan_no15_P;
5'd21: angle_tb <= (theta_sh[31]==1'b0)? atan_no16_N:atan_no16_P;
5'd22: angle_tb <= (theta_sh[31]==1'b0)? atan_no17_N:atan_no17_P;
5'd23: angle_tb <= (theta_sh[31]==1'b0)? atan_no18_N:atan_no18_P;
default:angle_tb <= 32'b0;
endcase
theta_sh_buf<=theta_sh;
zero <= (theta_sh==1'b0)? 1'b1:1'b0;
quadrant <= (cnt==5'd3)? ((theta_sh>PI_2)? 1'b1:1'b0):quadrant;
if(x[31]==1'b1)
begin
x_sh<=1'b0;
end
else
begin
case(cnt)
5'd7: x_sh <= {1'b0,x[31:1]};
5'd8: x_sh <= {2'b0,x[31:2]};
5'd9: x_sh <= {3'b0,x[31:3]};
5'd10: x_sh <= {4'b0,x[31:4]};
5'd11: x_sh <= {5'b0,x[31:5]};
5'd12: x_sh <= {6'b0,x[31:6]};
5'd13: x_sh <= {7'b0,x[31:7]};
5'd14: x_sh <= {8'b0,x[31:8]};
5'd15: x_sh <= {9'b0,x[31:9]};
5'd16: x_sh <= {10'b0,x[31:10]};
5'd17: x_sh <= {11'b0,x[31:11]};
5'd18: x_sh <= {12'b0,x[31:12]};
5'd19: x_sh <= {13'b0,x[31:13]};
5'd20: x_sh <= {14'b0,x[31:14]};
5'd21: x_sh <= {15'b0,x[31:15]};
5'd22: x_sh <= {16'b0,x[31:16]};
default: x_sh <= 32'b0;
endcase
end
if(y[31]==1'b1)
begin
y_sh<=1'b0;
end
else
begin
case(cnt)
5'd7: y_sh <= {1'b0,y[31:1]};
5'd8: y_sh <= {2'b0,y[31:2]};
5'd9: y_sh <= {3'b0,y[31:3]};
5'd10: y_sh <= {4'b0,y[31:4]};
5'd11: y_sh <= {5'b0,y[31:5]};
5'd12: y_sh <= {6'b0,y[31:6]};
5'd13: y_sh <= {7'b0,y[31:7]};
5'd14: y_sh <= {8'b0,y[31:8]};
5'd15: y_sh <= {9'b0,y[31:9]};
5'd16: y_sh <= {10'b0,y[31:10]};
5'd17: y_sh <= {11'b0,y[31:11]};
5'd18: y_sh <= {12'b0,y[31:12]};
5'd19: y_sh <= {13'b0,y[31:13]};
5'd20: y_sh <= {14'b0,y[31:14]};
5'd21: y_sh <= {15'b0,y[31:15]};
5'd22: y_sh <= {16'b0,y[31:16]};
default: y_sh <= 32'b0;
endcase
end
end
end
endmodule