FPGA SDR(19)自作ATAN2


2018/07/29 追記:360°を8等分して45°~90°に変換してから計算するようにすると音質が改善しました。
2018/07/22 追記:ATAN2の出力を16ビットまでパラメータで指定できるようにしました。

 

Cyclone II EP2C5T144で使えるQuartus II Web Edition 13.0sp1のMegaWizardにALTERA CORDICが見つかりません。EP2C5T144でもFMラジオを作りたいのでCORDICを勉強してATAN2を作ってみました。加減算とビットシフトだけで済んでしまうんですね。ATAN2の出力を16ビットとして、計算に必要なテーブルはたった13行です。

360°を8等分して45°~90°に変換してから計算しています。

`timescale 1ns/1ns


module MyATAN2
#(
	parameter XY_WIDTH = 10,
	parameter Q_WIDTH = 10
)
(
	input  wire       areset,
	input  wire       clk,
	input  wire       en,
	input  wire signed [XY_WIDTH-1:0] x,
	input  wire signed [XY_WIDTH-1:0] y,
	output reg signed [Q_WIDTH-1:0] q
);


	localparam TABLE_WIDTH = 16;
	localparam TABLE_SIZE = 13;
	localparam DATA_WIDTH = 1 + XY_WIDTH + TABLE_SIZE;
	localparam PI = 16'sb011_0010_0100_0011_1; // pi = 0011 . 0010 0100 0011 1111 0110 1010
	
	reg [3:0] n; // 0 ~ TABLE_SIZE+1
	reg [2:0] quad; // 0 ~ 7
	wire signed [DATA_WIDTH-1:0] pi;
	wire signed [DATA_WIDTH-1:0] pidiv2;
	reg signed [DATA_WIDTH-1:0] xn;
	reg signed [DATA_WIDTH-1:0] yn;
	reg signed [TABLE_WIDTH-1:0] atan;
	reg signed [TABLE_WIDTH-1:0] atan_table [0:TABLE_SIZE-1];


	initial begin
		atan_table[0]  = 16'sb000_1100_1001_0000_1; // atan(1/1):    000 . 1100 1001 0000 1111 1101 1010
		atan_table[1]  = 16'sb000_0111_0110_1011_0; // atan(1/2):    000 . 0111 0110 1011 0001 1001 1100
		atan_table[2]  = 16'sb000_0011_1110_1011_0; // atan(1/4):    000 . 0011 1110 1011 0110 1110 1011
		atan_table[3]  = 16'sb000_0001_1111_1101_0; // atan(1/8):    000 . 0001 1111 1101 0101 1011 1010
		atan_table[4]  = 16'sb000_0000_1111_1111_1; // atan(1/16):   000 . 0000 1111 1111 1010 1010 1101
		atan_table[5]  = 16'sb000_0000_0111_1111_1; // atan(1/32):   000 . 0000 0111 1111 1111 0101 0101
		atan_table[6]  = 16'sb000_0000_0011_1111_1; // atan(1/64):   000 . 0000 0011 1111 1111 1110 1010
		atan_table[7]  = 16'sb000_0000_0001_1111_1; // atan(1/128):  000 . 0000 0001 1111 1111 1111 1101
		atan_table[8]  = 16'sb000_0000_0000_1111_1; // atan(1/256):  000 . 0000 0000 1111 1111 1111 1111
		atan_table[9]  = 16'sb000_0000_0000_0111_1; // atan(1/512):  000 . 0000 0000 0111 1111 1111 1111
		atan_table[10] = 16'sb000_0000_0000_0011_1; // atan(1/1024): 000 . 0000 0000 0011 1111 1111 1111
		atan_table[11] = 16'sb000_0000_0000_0001_1; // atan(1/2048): 000 . 0000 0000 0001 1111 1111 1111
		atan_table[12] = 16'sb000_0000_0000_0000_1; // atan(1/4096): 000 . 0000 0000 0000 1111 1111 1111
	end
	
	
	assign pi = PI >>> (TABLE_WIDTH - Q_WIDTH);
	assign pidiv2 = pi >>> 1;

	always @(posedge clk)
	begin
		if (areset) begin
				n <= 0;

				quad <= 0;
				xn <= 0;
				yn <= 0;
				
				atan <= 0;
				
				q <= 0;
		end
		else begin				
			if (en) begin
				n <= 0;

				if      (x[XY_WIDTH-1] == 1'b0 && y[XY_WIDTH-1] == 1'b0) begin
					if (x >= y) begin
						quad <= 0;
						yn <= { x[XY_WIDTH-1], x, {TABLE_SIZE{1'b0}} };
						xn <= { y[XY_WIDTH-1], y, {TABLE_SIZE{1'b0}} };
					end
					else begin
						quad <= 1;
						xn <= { x[XY_WIDTH-1], x, {TABLE_SIZE{1'b0}} };
						yn <= { y[XY_WIDTH-1], y, {TABLE_SIZE{1'b0}} };
					end
				end
				else if (x[XY_WIDTH-1] == 1'b1 && y[XY_WIDTH-1] == 1'b0) begin
					if (-x <= y) begin
						quad <= 2;
						xn <= -{ x[XY_WIDTH-1], x, {TABLE_SIZE{1'b0}} };
						yn <= { y[XY_WIDTH-1], y, {TABLE_SIZE{1'b0}} };
					end
					else begin
						quad <= 3;
						yn <= -{ x[XY_WIDTH-1], x, {TABLE_SIZE{1'b0}} };
						xn <= { y[XY_WIDTH-1], y, {TABLE_SIZE{1'b0}} };
					end
				end
				else if (x[XY_WIDTH-1] == 1'b1 && y[XY_WIDTH-1] == 1'b1) begin
					if (-x >= -y) begin
						quad <= 4;
						yn <= -{ x[XY_WIDTH-1], x, {TABLE_SIZE{1'b0}} };
						xn <= -{ y[XY_WIDTH-1], y, {TABLE_SIZE{1'b0}} };
					end
					else begin
						quad <= 5;
						xn <= -{ x[XY_WIDTH-1], x, {TABLE_SIZE{1'b0}} };
						yn <= -{ y[XY_WIDTH-1], y, {TABLE_SIZE{1'b0}} };
					end
				end
				else begin
					if (x <= -y) begin
						quad <= 6;
						xn <= { x[XY_WIDTH-1], x, {TABLE_SIZE{1'b0}} };
						yn <= -{ y[XY_WIDTH-1], y, {TABLE_SIZE{1'b0}} };
					end
					else begin
						quad <= 7;
						yn <= { x[XY_WIDTH-1], x, {TABLE_SIZE{1'b0}} };
						xn <= -{ y[XY_WIDTH-1], y, {TABLE_SIZE{1'b0}} };
					end
				end
				
				atan <= 0;
				
				if      (quad == 0) q <= pidiv2 - atan; // x <-> y
				else if (quad == 1) q <= atan;
				else if (quad == 2) q <= pi - atan;
				else if (quad == 3) q <= pi - (pidiv2 - atan); // x <-> y
				else if (quad == 4) q <= -pi + (pidiv2 - atan); // x <-> y
				else if (quad == 5) q <= -pi + atan;
				else if (quad == 6) q <= -atan;
				else                q <= -(pidiv2 - atan); // x <-> y
			end
			else begin
				if (n <= TABLE_SIZE) begin
					n <= n + 1;
				end
				
				if (1 <= n && n <= TABLE_SIZE) begin
					if (yn[DATA_WIDTH-1] == 1'b0) begin
						xn <= xn + (yn >>> (n-1));
						yn <= yn - (xn >>> (n-1));
						atan <= atan + (atan_table[n-1] >>> (TABLE_WIDTH - Q_WIDTH));
					end
					else begin
						xn <= xn + (yn >>> (n-1));
						yn <= yn + (xn >>> (n-1));
						atan <= atan - (atan_table[n-1] >>> (TABLE_WIDTH - Q_WIDTH));
					end
				end
			end
		end
	end


endmodule