Продолжу "тихо сам с собою"... с надеждой, что может старейшины подправят, если что не то...
В ходе реализации (с фиксед поинт) пошла такая жосткая оптимизациия, вплоть до замены цикла на последовательные вычисления (благо порядок фильтра небольшой),
что вряд ли удалось бы воспользоватся предложенной структурой хидера.. так что все таки наверно судьба кроить хидеры самому, по своему разумению...
В конечном итоге один шаг фильтра уменшил с 349 проц. такта до 189. Причем в 349 уже было учтено, что нумераторы вида 1 0 -1, то есть вместо двух умножений и двух сложений біло использовано только одно вычитание.
Да, может кому сходится, заброшу сорцы сюда. Не сочтите за спам :-).
Итак, IIR, Direct-Form II, Second-Order Sections, Чебышева тип 1, 8-й порядок. Частота семплирования 400000, частоты среза среза 89000, 111000, неравномерность в полосе пропускания - 0.1 дБ.
с помощью fdaTool получены следующие коофициенты:
Код
% Generated by MATLAB(R) 7.12 and the Signal Processing Toolbox 6.15.
%
% Generated on: 04-Aug-2011 09:30:20
%
% Coefficient Format: Hexadecimal
% Discrete-Time IIR Filter (real)
% -------------------------------
% Filter Structure : Direct-Form II, Second-Order Sections
% Number of Sections : 4
% Stable : Yes
% Linear Phase : No
% Arithmetic : fixed
% Numerator : s16,15 -> [-1 1)
% Denominator : s16,15 -> [-1 1)
% Scale Values : s16,15 -> [-1 1)
% Input : s16,15 -> [-1 1)
% Section Input : s16,14 -> [-2 2)
% Section Output : s16,15 -> [-1 1)
% Output : s16,15 -> [-1 1)
% State : s16,15 -> [-1 1)
% Numerator Prod : s32,31 -> [-1 1)
% Denominator Prod : s32,31 -> [-1 1)
% Numerator Accum : s32,31 -> [-1 1)
% Denominator Accum : s32,31 -> [-1 1)
% Round Mode : convergent
% Overflow Mode : wrap
% Cast Before Sum : true
SOS matrix:
7fff 0000 8000 7fff 2f2d 751e
7fff 0000 8000 7fff d0d3 751e
7fff 0000 8000 7fff 12c3 6683
7fff 0000 8000 7fff ed3d 6683
Scale Values:
1835
1835
0fd3
0fd3
7e89
для справки, если делать для плавающей точки, то будет нижеследующее (как то проще представлять при просмотре, что единичный коэф. передачи это 1, а не 0x8000, а тем более 0xFFFF)
Код
SOS matrix:
0.999969482421875 0 -1 0.999969482421875 0.368560791015625 0.91497802734375
0.999969482421875 0 -1 0.999969482421875 -0.368560791015625 0.91497802734375
0.999969482421875 0 -1 0.999969482421875 0.146575927734375 0.800872802734375
0.999969482421875 0 -1 0.999969482421875 -0.146575927734375 0.800872802734375
Scale Values:
0.189117431640625
0.189117431640625
0.123626708984375
0.123626708984375
0.988555908203125
Ну и упомянутый хидер от fdaTool
Код
General type conversion for MATLAB generated C-code * /
#include "tmwtypes.h"
/ *
* Expected path to tmwtypes.h
* C:\Program Files\MATLAB\R2011a\extern\include\tmwtypes.h
* /
#define MWSPT_NSEC 9
const int NL[MWSPT_NSEC] = { 1,3,1,3,1,3,1,3,1 };
const int16_T NUM[MWSPT_NSEC][3] = {
{
6197, 0, 0
},
{
32767, 0, -32768
},
{
6197, 0, 0
},
{
32767, 0, -32768
},
{
4051, 0, 0
},
{
32767, 0, -32768
},
{
4051, 0, 0
},
{
32767, 0, -32768
},
{
32393, 0, 0
}
};
const int DL[MWSPT_NSEC] = { 1,3,1,3,1,3,1,3,1 };
const int16_T DEN[MWSPT_NSEC][3] = {
{
32767, 0, 0
},
{
32767, 12077, 29982
},
{
32767, 0, 0
},
{
32767, -12077, 29982
},
{
32767, 0, 0
},
{
32767, 4803, 26243
},
{
32767, 0, 0
},
{
32767, -4803, 26243
},
{
32767, 0, 0
}
};
Теперь моя реализация (сори за кривость идентификаторов, делалось второпях и пока без выработки оконечной идеологии наименований для проекта)
первым делом декларация коэфициентов
(еще раз извинения за то, что комментарии не на русском - влом приводить, должно быть понятно, если кто и досмотрит сюда)
Код
// Number of Sections;
#define NOS_IIR_Che1_89_1110_08 4
// Scale Values:
const int arrSV_IIR_Che1_89_1110_08[NOS_IIR_Che1_89_1110_08 + 1] =
{
0x1835, 0x1835, 0x0fd3, 0x0fd3, 0x7e89
};
// деномінатори:
//arrDen_IIR_Che1_89_1110_08array
const int arrDen_IIR_Che1_89_1110_08[NOS_IIR_Che1_89_1110_08][2] =
{
{ 12077, 29982},
{ -12077, 29982},
{ 4803, 26243},
{ -4803, 26243}
};
// робочі масиви
// декларую три окремих масиви, а не один двомірний дял того,
// щоби поиім можна було використати Copy і не думати про порядок обробки індексів
int arrW_IIR_Che1_89_1110_08[NOS_IIR_Che1_89_1110_08];
int arrW_old1_IIR_Che1_89_1110_08[NOS_IIR_Che1_89_1110_08];
int arrW_old2_IIR_Che1_89_1110_08[NOS_IIR_Che1_89_1110_08];
Обратите внимание, что номинаторов не дакларирую - они все вида 1 0 -1, посему будем долбить их гардкодингом
Далее цикловая реализация:
Код
//working, 558051/1599 = 349 циклів
//
inline InpF_IIR_Che1_89_1110_08_Step(int aInp)
{
int ns;
long int w0;
w0 = aInp; // тут aInp є в режимі s16, 15
//for ns := 0 to NOS_IIR_Che1_89_1110_08 - 1 do
for (ns =0; ns < NOS_IIR_Che1_89_1110_08; ns++ )
{
// тут w0 в s16, 15
w0 = w0 * (long int)(arrSV_IIR_Che1_89_1110_08[ns]);
// тут w0 в режимі s32, s30
// коеф. в реж. в s16, 15
// далі w[n] = x[n] - a1*w[n-1] - a2*w[n-2]
// a0 = 1.0, w0 пітримується в режимі, який допускає
// "пряме" використання w0
w0 = w0 // a0 = 1.0
- (long int)(arrW_old1_IIR_Che1_89_1110_08[ns]) * (long int)(arrDen_IIR_Che1_89_1110_08[ns][0])
- (long int)(arrW_old2_IIR_Che1_89_1110_08[ns]) * (long int)(arrDen_IIR_Che1_89_1110_08[ns][1]);
// !! використання тут функцій виду _smas виграшу не дає, напевно накладні витрати..
// готуємо до передачі в масиви затримки
arrW_IIR_Che1_89_1110_08[ns] = (w0 >> 15);
// далі y[n] = w[n] + b1*w[n-1] + b2*w[n-2]
// але тут b1 = 0 і b2 = -1
// тому взагалі далі робимо математику в варіанті s16, 15
w0 = (w0 >> 15) - arrW_old2_IIR_Che1_89_1110_08[ns];
// w0 = (arrW_IIR_Che1_89_1110_08[ns] = (w0 >> 15)) - arrW_old2_IIR_Che1_89_1110_08[ns];
}
// здійснюємо затримку
memcpy(&arrW_old2_IIR_Che1_89_1110_08, &arrW_old1_IIR_Che1_89_1110_08, sizeof(arrW_old2_IIR_Che1_89_1110_08));
memcpy(&arrW_old1_IIR_Che1_89_1110_08, &arrW_IIR_Che1_89_1110_08, sizeof(arrW_old1_IIR_Che1_89_1110_08));
return (w0 * (long int)arrSV_IIR_Che1_89_1110_08[NOS_IIR_Che1_89_1110_08]) >> 15;
}
как уже писал - 349 машинных циклов (тактов, вернее).
И для полноті инфо - безцикловая, которая дала 189
Код
// безциклова реалізація - з метою зменшення накладних витрат - цикл крутимо лише 4 рази, тому копи-паст...
// 307008/1599 -> 192
// 302211/1599 -> 189
inline InpF_IIR_Che1_89_1110_08_Step(int aInp)
{
int i16;
long int w0;
w0 = aInp; // тут aInp є в режимі s16, 15
// ---- "типу початок " циклу
// ns = 0 ****************************************************
#undef ns
#define ns 0
// тут w0 в s16, 15
// насамперед слід врахувати вхідний коеф. секції, а
// потім обробити деномінатори
// деномінатори обробляються за виразом
// w[n] = x[n] - a1*w[n-1] - a2*w[n-2], тут враховано що a0 = 1, і w0 підтримується в режимі,
// який допускаэ пряме використання w0 (незважаючи на те, що 1 відповідає 0x8000)
//
// вище сказане зводиться до двох наступних (закоментованих)операторів
//
// w0 = w0 * (long int)(arrSV_IIR_Che1_89_1110_08[ns]); // тут w0 вже в режимі s32, s30
// w0 = w0 // a0 = 1.0
// - (long int)(arrW_old1_IIR_Che1_89_1110_08[ns]) * (long int)(arrDen_IIR_Che1_89_1110_08[ns][0])
// - (long int)(arrW_old2_IIR_Che1_89_1110_08[ns]) * (long int)(arrDen_IIR_Che1_89_1110_08[ns][1]);
// але оптимізуючий компілятор дає кращий код, коли їх звести в один.
// Тому робимо це, незважаючи на деяку нечитабельність :-)
w0 = w0 * (long int)(arrSV_IIR_Che1_89_1110_08[ns]) // a0 = 1.0
- (long int)(arrW_old1_IIR_Che1_89_1110_08[ns]) * (long int)(arrDen_IIR_Che1_89_1110_08[ns][0])
- (long int)(arrW_old2_IIR_Che1_89_1110_08[ns]) * (long int)(arrDen_IIR_Che1_89_1110_08[ns][1]);
// далі готуємо до передачі в масиви затримки і обробляємо номінатори.
// це можна було би зробити наступними двома закоментованими операторами
// arrW_IIR_Che1_89_1110_08[ns] = (w0 >> 15);
// w0 = (w0 >> 15) - arrW_old2_IIR_Che1_89_1110_08[ns];
// але враховуючи особливості оптимізації зробимо це одлим, "нечитабельним":
w0 = (arrW_IIR_Che1_89_1110_08[ns] = ( w0 >> 15) )- arrW_old2_IIR_Che1_89_1110_08[ns];
// ns = 1 ****************************************************
#undef ns
#define ns 1
// коментарі див. в ns 0
w0 = w0 * (long int)(arrSV_IIR_Che1_89_1110_08[ns])
- (long int)(arrW_old1_IIR_Che1_89_1110_08[ns]) * (long int)(arrDen_IIR_Che1_89_1110_08[ns][0])
- (long int)(arrW_old2_IIR_Che1_89_1110_08[ns]) * (long int)(arrDen_IIR_Che1_89_1110_08[ns][1]);
w0 = (arrW_IIR_Che1_89_1110_08[ns] = ( w0 >> 15) ) - arrW_old2_IIR_Che1_89_1110_08[ns];
// ns = 2 ****************************************************
#undef ns
#define ns 2
// коментарі див. в ns 0
w0 = w0 * (long int)(arrSV_IIR_Che1_89_1110_08[ns])
- (long int)(arrW_old1_IIR_Che1_89_1110_08[ns]) * (long int)(arrDen_IIR_Che1_89_1110_08[ns][0])
- (long int)(arrW_old2_IIR_Che1_89_1110_08[ns]) * (long int)(arrDen_IIR_Che1_89_1110_08[ns][1]);
w0 = (arrW_IIR_Che1_89_1110_08[ns] = ( w0 >> 15) ) - arrW_old2_IIR_Che1_89_1110_08[ns];
// ns = 3 ****************************************************
#undef ns
#define ns 3
// коментарі див. в ns 0
w0 = w0 * (long int)(arrSV_IIR_Che1_89_1110_08[ns])
- (long int)(arrW_old1_IIR_Che1_89_1110_08[ns]) * (long int)(arrDen_IIR_Che1_89_1110_08[ns][0])
- (long int)(arrW_old2_IIR_Che1_89_1110_08[ns]) * (long int)(arrDen_IIR_Che1_89_1110_08[ns][1]);
w0 = (arrW_IIR_Che1_89_1110_08[ns] = ( w0 >> 15) ) - arrW_old2_IIR_Che1_89_1110_08[ns];
// ---- "типу кінець " циклу
// використання тут 16 бітної зміннох заміть 32 бітної w0 дозволяє зекономити декілька циклів (точно не задокоментував)
i16 = (w0 * (long int)arrSV_IIR_Che1_89_1110_08[NOS_IIR_Che1_89_1110_08]) >> 15;
memcpy(&arrW_old2_IIR_Che1_89_1110_08, &arrW_old1_IIR_Che1_89_1110_08, sizeof(arrW_old2_IIR_Che1_89_1110_08));
memcpy(&arrW_old1_IIR_Che1_89_1110_08, &arrW_IIR_Che1_89_1110_08, sizeof(arrW_old1_IIR_Che1_89_1110_08));
//return (w0 * (long int)arrSV_IIR_Che1_89_1110_08[NOS_IIR_Che1_89_1110_08]) >> 15;
return i16;
}
Фсе... Может кому сгодится. Но больше приветсвуются комментарии, в тч лехкое мордобитие...
пс. копи паст на дефайн менять как то влом было, тем болеее что тогда с читабельностью совсем тяжело...