четверг, 31 октября 2013 г.

CORDIC Verilog - продолжение

Продолжение этого поста, где был написан модуль вычисления значений синуса и косинуса в заданном угле методом CORDIC.
Сегодняшней задачей будет сравнить получившиеся выходные значения квадратур с идеальными значениями синуса и косинуса, рассчитанными в заданных точках. Я буду приводить данные только для канала Re_out, потому что результаты будут одинаковыми для обоих каналов. Я хочу получить значения Re_out в текстовый файл. Для этого перепишем test bench:
Exported from Notepad++
module cordic_tb; reg clk; reg signed [ 14 : 0 ] arg; wire signed [ 15 : 0 ] Re_out; wire signed [ 15 : 0 ] Im_out; localparam N = 12; cordic_phase u0 ( .clk(clk), .arg(arg), .Re_out(Re_out), .Im_out(Im_out) ); integer re; integer im; initial begin clk = 0; arg = 0; re = $fopen("re.txt", "w"); im = $fopen("im.txt", "w"); while(arg != 16383) begin arg = arg + 1; if(arg > N+3) begin $fwrite(re, "%d\n", Re_out); $fwrite(im, "%d\n", Im_out); end @(posedge clk); end $finish; $fclose(re); $fclose(im); end always #10 clk = !clk; endmodule

































Создадим два файла, куда запишется один период квадратур. Начнем писать спустя 15 тактов, потому что на этот такт появится первое правильное значение. До этого симулятор покажет на выходе что-то вроде 'XXX', в реальном железе будет скорее всего '0'. Так мы получили текстовый файл с результатами работы модуля cordic_phase.
Для построения графиков я буду пользоваться программой FreeMat, которая является легковесной кроссплатформенной альтернативой матлабу и октаву.
Во-первых загрузим текстовый файл из test bench во FreeMat.
load('re.txt')
Во-вторых посчитаем косинус в тех же точках, что и в test bench:
for n = 1:1:16384
my_re(n) = fix(32767*cos(360*(n-1)*pi/(180*16384)));
end
Построим оба графика в одной сетке:
plot(my_re)
hold on;
plot(re)
Графики накладываются, но если приблизить масштаб увидим:
Зеленый - CORDIC Verilog
Синий - FreeMat
Видно, что в каких-то точках графики соприкасаются, но потом расходятся. Это недостаток самого метода - мы не можем представить все углы непрерывно потому, что у нас дискретный и конечный (всего 12) набор углов. 
Теперь посмотрим на абсолютную ошибку между двумя графиками:
for n = 1:1:16384
delta(n) = my_re(n)-re(n);
end
Тут будут проблемы из-за того, что длина re не 16384, потому что из начала 15 отсчетов выкинули, а в конце не добавили. Так что будем верить результатам в первых 99/100 графика. 

Видно, что ошибка симметричная и достигает почти 70 в "плохих" углах. Это график абсолютной погрешности. На самом деле видно, что ошибка растет в областях, где косинус проходит 0. Я уже писал, что ошибка там будет расти из-за операции сдвига, вместо деления, когда отрицательное число всегда при сдвиге остается отрицательным.

С относительно погрешностью как-то сложнее. Если построить график для 10000 точек:
for n = 1:1:10000
delta(n) = double(abs(my_re(n)-re(n))/ abs(my_re(n)));
end


Адский скачок в точка с абсциссой 4097, равный 24. Это точка - угол 90 градусов, где должен был быть ноль. Природа этой проблемы заключается в том, что точное значение угла достигается на 11 шаге, а в 12 вносится искажение. В точках 1..3000 максимальная относительная погрешность составляет 0,24%, В точках 1..4000 - 2,63%. В точках 4000..4090 - 23,89%. Видно, чем ближе к нулю, тем хуже. Апофеоз всего этого действа 124%.

Комментариев нет:

Отправить комментарий