URL
https://opencores.org/ocsvn/cordic_atan_iq/cordic_atan_iq/trunk
Subversion Repositories cordic_atan_iq
Compare Revisions
- This comparison shows the changes necessary to convert path
/cordic_atan_iq
- from Rev 1 to Rev 2
- ↔ Reverse comparison
Rev 1 → Rev 2
/AuxClasses.sv
0,0 → 1,43
`ifndef _AuxClasses_ |
`define _AuxClasses_ |
|
package AuxClassesPkg; |
|
const real PI = 3.1415926535897932384626433832795; |
|
const int V_MAX = {{3{1'b0}}, {29{1'b1}}}; |
const int V_MIN = {{3{1'b1}}, {29{1'b0}}}; |
|
class IQClass; |
int I, Q; |
|
function new(int I, int Q); |
this.I = I; |
this.Q = Q; |
endfunction |
endclass :IQClass |
|
class IQArrayList; |
IQClass data[$]; |
|
function new(); |
data = {}; |
endfunction |
|
function void push_back(int I, int Q); |
IQClass iq = new(I, Q); |
data.push_back(iq); |
endfunction |
|
function int size(); |
return data.size; |
endfunction |
|
function string ToString(); |
return ""; |
endfunction |
endclass :IQArrayList |
|
endpackage :AuxClassesPkg |
|
`endif |
/AuxFunc.sv
0,0 → 1,82
`ifndef _AuxPkg_ |
`define _AuxPkg_ |
`include "AuxClasses.sv" |
|
package AuxFuncPkg; |
import AuxClassesPkg::*; |
export AuxClassesPkg::*; |
|
function automatic int atan_iq_int(int IS, int QS); |
if (IS == 0) |
if (QS > 0) |
return 2**30; // 90 |
else if (QS < 0) |
return -(2**30); // -90 |
else |
return 0; |
else if (QS == 0) |
if (IS < 0) |
return 1<<31; // -180 |
else |
return 0; |
else if (IS < 0) |
if (QS > 0) |
return int'(($atan(real'(QS)/IS) + PI) * ((2.0**31) / PI)); |
else |
return int'(($atan(real'(QS)/IS) - PI) * ((2.0**31) / PI)); |
else |
return int'($atan(real'(QS)/IS) * ((2.0**31) / PI)); |
endfunction |
|
function automatic real int_to_deg(int angle); |
return real'(angle) / 2.0**30 * 90.0; |
endfunction |
|
function automatic int delta(int ref_angle, int angle); |
return angle - ref_angle; |
endfunction |
|
function automatic real rel_pct(int ref_angle, int angle); |
int d = delta(ref_angle, angle); |
|
if (d < 0) d = -d; |
|
if (ref_angle == 0) |
if (d == 0) |
return 0; |
else |
return 100; |
else |
return real'(d) / real'(ref_angle) * 100.0; |
endfunction |
|
function automatic IQArrayList GenIQ(int POINTS = 8); |
IQArrayList v = new; |
|
// 135...45 deg |
for (int i = -POINTS; i < POINTS; i++) |
v.push_back(i * (V_MAX / POINTS), V_MAX); |
|
// 45...-45 deg |
for (int i = POINTS; i > -POINTS; i--) |
v.push_back(V_MAX, i * (V_MAX / POINTS)); |
|
// -45...-135 deg |
for (int i = POINTS; i > -POINTS; i--) |
v.push_back(i * (V_MAX / POINTS), V_MIN); |
|
// -135...135 deg |
for (int i = -POINTS; i < POINTS; i++) |
v.push_back(V_MIN, i * (V_MAX / POINTS)); |
|
return v; |
endfunction |
|
function automatic int unsigned absi(int value); |
if (value < 0) value = -value; |
return value; |
endfunction |
|
endpackage :AuxFuncPkg |
|
`endif |
/atan32_table_gen.m
0,0 → 1,47
clc |
|
i = 0; |
atan_table(1) = int32(1); |
|
while i == 0 || atan_table(i) ~= 0 |
atan_table(i+1) = int32(round(atan(1/2^i) / (pi/2) * 2^30)); |
i = i + 1; |
end |
|
k = 1; |
for i=1:length(atan_table) % ? |
k = k * 1 / sqrt(1 + 2^(-2 * (i-1))); |
end |
|
fid = fopen('atan32_table.sv', 'w'); |
|
fprintf(fid, '`ifndef _atan32_table_\n'); |
fprintf(fid, '`define _atan32_table_\n'); |
fprintf(fid, '// atan table for values 1, 1/2, 1/4, 1/8, 1/16...\n'); |
fprintf(fid, '// Scale: code 2^30 = 90 deg, code 0 = 0 deg\n\n'); |
|
fprintf(fid, 'package ConstPkg;\n\n'); |
fprintf(fid, ' parameter STEPS = %d;\n', length(atan_table)); |
fprintf(fid, ' parameter real K = %g;\n', k); |
fprintf(fid, ' parameter int K_INT = 32''h%08x;\n', int32(round(k * 2^30))); |
fprintf(fid, '// for sin and cos start calculation from vector {K_INT, 0}\n'); |
fprintf(fid, '// last table value dont use for atan calculation\n\n'); |
|
fprintf(fid, ' bit signed [31:0] atan_table[STEPS] = ''{\n'); |
|
for i=1:length(atan_table) |
fprintf(fid, ' 32''sh%08x', atan_table(i)); |
if i ~= length(atan_table) |
fprintf(fid, ', // tan = 1/2^%d = 1/%d\n', i, 2^i); |
else |
fprintf(fid, ' // tan = 1/2^%d = 1/%d\n', i, 2^i); |
fprintf(fid, ' };\n\n'); |
end |
end |
|
fprintf(fid, 'endpackage: ConstPkg\n\n'); |
fprintf(fid, '`endif\n'); |
|
fclose(fid); |
|
sum(double(atan_table / 2^30)) |
/cordic_atan_iq.m
0,0 → 1,73
|
function angle = cordic_atan_iq(IS, QS); |
|
IS = int32(IS); |
QS = int32(QS); |
|
tg = double(QS) / double(IS); |
atg = atan(tg) / pi * 180; |
fprintf('Given I = %g, Q = %g, angle = %g degree\n', IS, QS, atg); |
|
sign = IS < 0; |
if sign |
IS = -IS; |
QS = -QS; |
end |
|
% [F Exp_I] = log2(double(IS)); |
% [F Exp_Q] = log2(double(QS)); |
% |
% Exp_I |
% Exp_Q |
% |
% E = max(Exp_I, Exp_Q) + 1; |
% |
% IS = int32(IS * 2^(31 - E)); |
% QS = int32(QS * 2^(31 - E)); |
|
fprintf('I = 0x%08x, Q = 0x%08x\n', IS, QS); |
|
i = 0; |
atg_table(1) = int32(1); |
|
while i == 0 || atg_table(i) ~= 0 |
atg_table(i+1) = int32(2^30 * atan(1/2^i) / (pi/2)); |
i = i + 1; |
end |
|
x(1) = IS; |
y(1) = QS; |
a(1) = int32(0); |
|
tg = double(y(1)) / double(x(1)); |
atg = atan(tg) / pi * 180; |
fprintf('i = %d: x = %d, y = %d (%g deg), a = %d(%g degree)\n', 0, x(1), y(1), atg, a(1), double(a(1))/2^30 * 90); |
|
for i=2:length(atg_table) % i dont use last table element |
if y(i-1) > 0 |
x(i) = x(i-1) + y(i-1) / 2^(i-2); |
y(i) = -x(i-1) / 2^(i-2) + y(i-1); |
a(i) = a(i-1) - atg_table(i-1); |
fprintf('i = %d: rot = %g deg, ', i-1, -double(atg_table(i-1)) / 2^30 * 90); |
else |
x(i) = x(i-1) - y(i-1) / 2^(i-2); |
y(i) = x(i-1) / 2^(i-2) + y(i-1); |
a(i) = a(i-1) + atg_table(i-1); |
fprintf('i = %d: rot = %g deg, ', i-1, double(atg_table(i-1)) / 2^30 * 90); |
end |
|
tg = double(y(i)) / double(x(i)); |
atg = atan(tg) / pi * 180; |
fprintf('x = %d, y = %d (%g deg), a = %d(%g deg)\n', x(i), y(i), atg, a(i), double(a(i))/2^30 * 90); |
end |
|
angle = -double(a(end))/2^30 * 90; |
if sign |
if angle > 0 |
angle = angle - 180; |
else |
angle = angle + 180; |
end |
end |
|
end |
/cordic_atan_iq.sv
0,0 → 1,57
`ifndef _cordic_atan_iq_ |
`define _cordic_atan_iq_ |
`include "atan32_table.sv" |
|
module cordic_atan_iq(clk, IS, QS, angle); |
import ConstPkg::atan_table; |
localparam STEPS = ConstPkg::STEPS; // todo: add to m gen |
|
input wire clk; // todo: clk_ena |
input wire signed [29:0] IS, QS; |
output reg signed [31:0] angle; |
|
wire sign; |
reg [STEPS-1:0] sign_dly; |
reg signed [31:0] x[STEPS], y[STEPS]; // + 1 bit for carry |
reg signed [31:0] a[STEPS]; |
|
assign sign = IS < 'sh0; |
|
always_ff @(posedge clk) begin |
x[0] <= (sign) ? 32'sh0 - IS : 32'sh0 + IS; |
y[0] <= (sign) ? 32'sh0 - QS : 32'sh0 + QS; |
a[0] <= '0; |
sign_dly <= {sign_dly[STEPS-2:0], sign}; // remember the sign |
end |
|
genvar i; |
generate for(i = 1; i < STEPS; i++) |
begin :gen |
always_ff @(posedge clk) |
if (y[i-1] > 'sh0) |
begin |
x[i] <= x[i-1] + (y[i-1] >>> (i - 1)); |
y[i] <= y[i-1] - (x[i-1] >>> (i - 1)); |
a[i] <= a[i-1] - atan_table[i-1]; |
end |
else |
begin |
x[i] <= x[i-1] - (y[i-1] >>> (i - 1)); |
y[i] <= y[i-1] + (x[i-1] >>> (i - 1)); |
a[i] <= a[i-1] + atan_table[i-1]; |
end |
end |
endgenerate |
|
always_ff @(posedge clk) |
if (sign_dly[STEPS-1]) |
if (a[STEPS-1] <= 'sh0) |
angle <= {1'b1, 31'h0} - a[STEPS-1]; |
else |
angle <= {1'b0, {31{1'b1}}} - (a[STEPS-1] - 1'b1); |
else |
angle <= -a[STEPS-1]; |
|
endmodule : cordic_atan_iq |
|
`endif |
/cordic_atan_iq_tb.sv
0,0 → 1,108
timeunit 1ns; |
timeprecision 1ns; |
|
`include "AuxFunc.sv" |
`include "AuxClasses.sv" |
|
module cordic_atan_iq_tb; |
import AuxFuncPkg::*; |
import AuxClassesPkg::*; |
|
bit clk = 0; |
bit signed [29:0] IS, QS; |
wire signed [31:0] angle; |
|
int cur_delta, delta_max = 0, delta_min = 0; |
real pct; |
int i, j; |
|
always #10ns clk++; |
|
initial begin |
repeat(10) @(posedge clk); |
|
//Test(20_000_000, 4_000_000); |
//Test(0, V_MAX); |
//Test(V_MIN, 0); |
//Test(V_MIN, V_MAX); |
//Test(0, V_MIN); |
Test2(); |
|
$stop(2); |
end |
|
cordic_atan_iq dut(.*); |
|
task Test(bit signed [29:0] I, Q); |
int atan_int; |
|
IS = I; |
QS = Q; |
repeat(100) @(posedge clk); |
|
atan_int = atan_iq_int(int'(IS), int'(QS)); |
cur_delta = delta(atan_int, angle); |
pct = rel_pct(atan_int, angle); |
|
$display("%d {%08h, %08h}: Ref %g deg (%d), RTL %g deg (%d), delta %d (%g pct)", i, int'(IS), int'(QS), int_to_deg(atan_int), atan_int, int_to_deg(angle), angle, cur_delta, pct); |
|
assert (absi(cur_delta) < 100) else $display("ERROR!"); |
endtask |
|
task Test2(); |
int atan_int; |
int I, Q; |
automatic bit error = 0; |
|
// automatic IQArrayList v = GenIQ(); |
automatic IQArrayList v = GenIQ(1024); |
|
fork |
begin |
for (int i = 0; i < v.size(); i++) begin |
IS = v.data[i].I; |
QS = v.data[i].Q; |
@(posedge clk); |
end |
end |
begin |
repeat(32+2) @(posedge clk); |
|
for (int i = 0; i < v.size(); i++) begin |
I = v.data[i].I; |
Q = v.data[i].Q; |
|
atan_int = atan_iq_int(I, Q); |
|
cur_delta = delta(atan_int, angle); |
if (cur_delta > delta_max) delta_max = cur_delta; |
if (cur_delta < delta_min) delta_min = cur_delta; |
|
pct = rel_pct(atan_int, angle); |
|
$display("%d {%08h, %08h}: Ref %g deg (%d), RTL %g deg (%d), delta %d (%g pct)", i, I, Q, int_to_deg(atan_int), atan_int, int_to_deg(angle), angle, cur_delta, pct); |
|
assert (absi(cur_delta) < 100) |
else |
begin |
error = 1; |
$display("ERROR!"); |
end |
|
@(posedge clk); |
end |
|
if (error) |
$display("Result: ERROR!"); |
else |
$display("Result: OK"); |
end |
join |
|
$display("Calculation precision: [%d, %d] ([%g, %g] sec)", delta_min, delta_max, int_to_deg(delta_min) * 60**2, int_to_deg(delta_max) * 60**2); |
|
repeat(100) @(posedge clk); |
$stop(2); |
endtask |
|
endmodule :cordic_atan_iq_tb |
/cordic_atan_test.m
0,0 → 1,4
clc |
|
angle = cordic_atan_iq(20e6, 4e6) |
|