趣味で非パイプラインのCORDICの実装

個人的に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


```