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