4.11 - DCF IIR исследования ч.2

После не очень удачных попыток (2.10 - VCF IIR исследования) было проделано много работы по изучению матчасти. В целом, решено пока остановиться на БИХ, второго порядка, и если все будет нормально, увеличить до четвертого. Так же рассматривались КИХ, но делать пока не стал, т.к. не основная задача. И так потеряно много времени, ждет решения МИДИ вход на оптронах.

Проблемы в построении в первой части состояли в том, что FLOAT все заработало почти сразу. Применить FLOAT на ПЛИС оказалось очень ресурсоемкой и не решаемой для меня задачей (не нашел я библиотеки для FLOAT). К тому же общественность на форумах пишет о применении FIXED POINT. Запустить FIXED с ходу не получилось - не хватило мозгов, но в целом суть следующая: все вычисления масштабируются. Коэффициенты, входные данные. То есть все умножается, например, на какую-то степень двойки, а потом делится обратно. Степень двойки, потому что это простые сдвиги.

К примеру, число 2.5 можно представить в фиксированной точке так:

- допустим, под целую часть будет 1 байт и под дробную тоже

- значит целая часть будет 02ХХh (дробную часть пока не посчитали). ну либо сразу 2 умножаем на 256 - будет 0200h

- дробная часть это 0.5 - умножаем ее на 256 (2^8) получается 128 (то есть половина от 256, короче 0.5) - это 0080h

- складывам целую и дробные части - получаем 0280h. Либо вообще сразу 256*2.5 и получаем 640 (0280h)

- при перемножении двух чисел, сдвинутых на 32 бита влево, результат нужно сдвигать обратно вправо. причем сначала присвоить регистру бОльшей ширины, иначе старшие биты будут потеряны.

Однако, размер чисел был выбран по 64 бита. На дробную часть 32 бита. Возможно можно что-то и сократить в будущем, например, целую часть, но пока так. Проблема с FLOAT получилась решена, но появилась другая проблема: при сборке проекта с умножением оказались потрачены все 46 из 46-ти аппаратных умножителей. И еще никак не удавалось исправить проблему с возбудом фильтра: фильтр работал, но не долго.

Вспомнилось трудное детство и деление через умножение. И все это через сдвиги битов. Если немного подумать, то все умножения можно заменить суммой сдвигов, один раз просчитав их количество. Ведь множители в фильтре - константы!

Например, надо умножить 2.5 на 5.5, чтоб получилось 13.75

заходим в калькулятор http://www.golovchenko.org/cgi-bin/constdivmul

Вводим 16 бит и умножение на 5.5 получаем

он нам выдает

ALGORITHM:

; Clear accumulator

; Add input * 4 to accumulator

; Add input * 1 to accumulator

; Add input / 2 to accumulator

; Move accumulator to result

то есть мы любое число можем теперь умножать на 5.5.

2.5 переводим в fixed. умножаем на 2^8 ( 256) получаем 640. переведем в HEX

видим: 0280h то есть целая часть 02 - все верно, дробная 80h (то есть 127, то есть половина от 256, короче 0.5)

теперь согласо алгоритму. но уже на верилоге

reg [15:] accum, a;

a = 16'h0280; //это 2.5

//теперь умножение на 5.5

accum = 0; //очистили

accum = accum + (a << 2); //это мы а*4 путем сдвига влево на 2 бита

accum = accum + a; //это мы а добавили еще раз к аккуму.

//получилось 4 раза А и еще один раз А. то есть 5 раз. целая часть готова

//а теперь добавляем половину от А. делим его на 2 и добавляем

accum = accum + (a >> 1); //поделили путем сдвига вправо на 1 бит

//все результат будет 0DC0h то есть сразу видно, что D это 13

//а в дроби перевести: 0DC0h это 3520dec сдвигаем на 8бит обратно.

//то есть делим на 256. получаем 3520/256 = 13,75 все :)

Табличка для перевода деление->сдвиг

/2 >>> 1

/4 >>> 2

/8 >>> 3

/16 >>> 4

/32 >>> 5

/64 >>> 6

/128 >>> 7

/256 >>> 8 (2^8)

/512 >>> 9

/1028 >>> 10

/2048 >>> 11

/4096 >>> 12

/8192 >>> 13 (2^13)

/16384 >>> 14

/32768 >>> 15

/65536 >>> 16

/131072 >>> 17

/262144 >>> 18

/524288 >>> 19

/1048576 >>> 20

/2097152 >>> 21

/4194304 >>> 22

/8388608 >>> 23

/16777216 >>> 24

/33554432 >>> 25

/67108864 >>> 26

/134217728 >>> 27

/268435456 >>> 28

/536870912 >>> 29

/1073741824 >>> 30

/2147483648 >>> 31

/4294967296 >>> 32

Концепт фильтра - верилог.

Еще написать про исправление проблем синхронизации, которые так никуда и не делись.

Фильтр рассчитан на частоту дискретизации 25600 Гц (что в 256 раз больше частоты среза - это для простоты рассчета)

Если пропустить меандр 100 Гц через фильтр, настроеный на 100 Гц, то получается:

Если пропустить белый шум через фильтр 100 Гц и переключать его кнопкой в режим 440 Гц, то получится такой спектр.

Записи звуков ниже. Теперь надо делать отдельным модулем.

Проблемы с синхронизацией никуда не делись, к тому же фильтры больше 2го порядка не работают даже в эмуляции. Есть мнение, что фильтр работает "странно", поэтому пересчитан с 25600Гц на 102400 Гц для 100 Гц среза. Но снова появились проблемы синхронизации. Поэтому пробую сделать "канонический" фильтр, в котором больше хранения данных, но математики больше и она более очевидна. В float уже получилось.

Важное замечание: c1, c2... стоят со знаком минус, поэтому результат нужно вычитать (см. sum2).

real states1 [0:4]; //4 элемента

real states2 [0:4]; //2 элемента

real znum [0:4]; //5 элементов

real zden [0:4]; //4 эл

real sumden, sumnum;

states1[0]=0;

states1[1]=0;

states1[2]=0;

states1[3]=0;

states2[0]=0;

states2[1]=0;

states2[2]=0;

states2[4]=0;

znum[0]=1.395012007e-09;

znum[1]=5.580048027e-09;

znum[2]=8.370072041e-09;

znum[3]=5.580048027e-09;

znum[4]=1.395012007e-09;

zden[0]= 0.968440614;

zden[1]=-3.904818199;

zden[2]= 5.904309825;

zden[3]=-3.967932218;

zden[4]= 1.0;

always @(posedge clk) begin

sum1 = sign8 * znum[4]

+ states1[0] * znum[3]

+ states1[1] * znum[2]

+ states1[2] * znum[1]

+ states1[3] * znum[0];


//он же резалт

sum2 = sum1 * zden[4]

- states2[0] * zden[3]

- states2[1] * zden[2]

- states2[2] * zden[1]

- states2[3] * zden[0];


states1[3] <= states1[2];

states1[2] <= states1[1];

states1[1] <= states1[0];

states1[0] <= sign8;

states2[3] <= states2[2];

states2[2] <= states2[1];

states2[1] <= states2[0];

states2[0] <= sum2;

end

Дальше этот код надо 1) перевести на fixed 2) опробовать порядки выше 2-го 3) решить проблемы с синхронизацией.