Comments 36
Магия))
Texas Instruments
«не используя операций умножения и деления»
(r7 >> 1) — (r7 >> 6)
r7/2 — r7/64 = r7/2.065
Приведённый отрывок это программная реализация умножения (или деления — как угодно) с фиксированной точкой на конкретную константу.
Умножение не перестаёт быть умножением считаете вы его в столбик или на калькуляторе.
Но этот код дизассемблирован и сконвертирован на C с целью _изучить его работу_.
Моё естественное желание _обьяснить_ детали реализации натолкнулость на непонятное сопротивление.
Видимо, согласно первому комменту, людям проще поверить в магию, чем в возможность реализации умножения через сложение/сдвиг.
http://users.utcluj.ro/~baruch/book_ssce/SSCE-Shift-Mult.pdf
Посмотрите сами, для реализации битового сдвига нам понадобится:
— Сдвиговый регистр
— 1 такт на сдвиг
Если есть возможность скоммутировать выходы регистра — достаточно обычного регистра.
Для реализации умножения нам понадобится:
— Два сдвиговых регистра
— Регистр под результат
— Сумматор
— 4 такта на умножение
Про то, что комбинационная схема сумматора будет в разы медленнее, чем сдвиговый регистр, я даже говорить не буду.
А кто-то спорит?
Я лишь показал как именно осуществляется умножение/деление, для тех кому интересно.
Причём тут ваш комментарий про «затратность»? Где-то в моём комментарии утверждалось иное?
Я говорил что мною приведённый кусок кода это не более чем оптимизированное умножение на константу.
>> 1 такт на сдвиг
Зависит от процессора. В Pentium4 сдвиг — 4 такта.Power4/PPC970 — 2 такта.
В иных сдвиг может приводить к вызову микрокода.
Чтобы был 1 такт на сдвиг (на несколько бит) ваша схема с одним сдвиговым регистром не подходит.
А вот barrel-shifter в свою очередь это довольно массивная схема, в отличие от последовательного умножителя.
Вот тут хорошо видно, что сдвигатель больше чем _всё_ ALU и пара регистров вместе взятых.
http://daveshacks.blogspot.ru/2016/01/inside-armv1-decoding-barrel-shifter.html
Про то, что комбинационная схема сумматора будет в разы медленнее, чем сдвиговый регистр, я даже говорить не буду.Не спорю, умножение в общем виде гораздо затратнее этой оптимизации.
Но, ради истины, здесь два сдвига, а между ними стоит вычитание (по сложности равно сложению).
Некорректно говорить, что тут умножение заменено одним лишь сдвигом.
Просто надо всегда понимать, что происходит. На той же современной x86 32-битное умножение быстрее работает, чем 2 сдвига и 3 сложения.
По аналогии считаем рубли, копейки отбрасываем.
Битовый сдвиг всегда выполняется за один такт.
Помнится, некоторые варианты процессоров выполняли битовый сдвиг в цикле.
Битовый сдвиг != умножение.
да ну? а помоему битовый сдвиг дает те же результаты что умножение/деление на степень двойки.
В случае, если нет математического сопроцессора, умножение может занимать далеко за десяток тактов.
Первое что приходит в голову — avr, нет сопроцессора, но есть однотактная умножалка 8*8. К слову, подкласс сигнальных процессоров (особенно древние) как правило имеет умножалку на борту но не имеет математический сопроцессор.
Может недостаточно точно выразился… Имелась в виду операция в смысле стандарта языка «Умножение — арифметическая бинарная операция ...»
Из-за прогрессирующего многоу́ровневого абстракционизма программисты забывают что на самом деле они не «пишут программы», а отдают приказы процессору. Умение дать такой приказ, чтобы он был выполнен эффективно и в срок, и есть искусство управления. В TI есть эффективные менеджеры (без кавычек) по управлению процессорами.
Таким образом, по виду фазовой характеристики можно сделать вывод, что данное звено является звеном 5 порядка.
А докам, значит, вы не склонны верить — мало ли что они там понапишут чтобы специально запутать:
; Freq_Stop: 2.5KHz, Attenuation_Stop: 40dB
; Freq_Pass: 1.4KHz, Attenuation_Pass: 1dB
; Order of filter = 5
Ну и ради исследовательского эксперимента можно в любой тулзе по проектированию фильтров (хоть тот же filterBuilder в Матлабе, да сотни их) вбить эти характеристики и поиграться с типами, в том числе и Баттерворта.
Человек способны в «рукопашную» разобрать происходящие процессы, а равно как и способный доходчиво объяснить. Достоин, как минимум, уважения.
А ваш способ, больше годится для работы. Т.е. когда надо просто подобрать компоненты или в поисках проблемы, если таковая возникнет… ну и прочее.
Так, что автору спасибо. Просвещайте дальше. Может появятся ещё инженеры которые понимают, что делают, а не лепят типовые решения.
Из анализа импульсной характеристики видно, что фильтр не лишен недостатков. Импульсная характеристика содержит незатухающие колебания, амплитудой одна дискрета.Это не недостаток, а фича — это же БИХ. При желании, этот бесконечный хвост можно было бы обрезать, добавив соответствующую логику.
"Недостатки" скорее всего вызваны округлением коэффициентов фильтра до 2^(-n), чтобы стало возможным отказаться от использования умножения и деления, и заменить их на операции сдвига.
А что за форум? Английский? Ссылку не дадите — поделюсь с англоязычными коллегами
С одной стороны умные ребята работают в TI, с другой стороны достаточную документацию не пишут. Страдал в свое время работая с их ЦСП.
OpenMP + ndk на 6678 по отдельности работают замечательно. Вместе требуют притирки в виде правильной инициализации менеджера очереди qmss. При этом из документации есть только пример с OpenMP 1.0 и работа с семафорами. Нормальная инициализация найдена через 2 недели на полу открытом git хранилище. При этом нужно поправить немало исходников mcsdk (в гайдах написано неподходящее решение), а уж про добавление какого-нибудь srio к первым двум технологиям и говорить страшно. Про забагованность ccs и говорить не хочется, но стоит отдать должное, что компилятор и линковщик замечательно описаны в гайдах.
Великолепно! Формулы, как обычно, вызывают печаль (у меня, так как забыты уже чуть менее, чем полностью за невостребованностью), но читал как детектив.
развернутая реализация для вещественных чисел:
double filtd(double x)
{
static double fltmem[5];
double out_f = (-1.515625*fltmem[4]-1.109375*fltmem[2]+0.515625*fltmem[0]+0.109375*x)*.5;
double new_flt3 = -(fltmem[4]*0.515625+fltmem[0]*0.484375);
fltmem[4] = fltmem[3];
fltmem[3] = new_flt3;
//
double new_flt1 = -(0.109375*fltmem[2]+0.890625*x);
fltmem[2] = fltmem[1];
fltmem[1] = new_flt1;
fltmem[0] = x;
return out_f;
};
Тут дана функция, ок. Допустим, у нас есть некий АЦП, на выходе имеем некий массив с отсчётами этого АЦП за определённый период времени, дальше что? Скармливать этой функции по одному значению из массива?
Простите если туплю, я пока только начинаю в цифровой обработке разбираться.
Вызывая данную функцию с частотой F и передавая ей в качестве аргумента входной сигнал, на выходе мы получим выходной сигнал, соответствующий реакции фильтра с указанными частотными характеристиками, частота среза по уровню 0.707 будет равняться 0,25*F
еще раз, другими словами
АЦП опрашивается с частотой 1кГц
значения АЦП скармливаюся функции фильтра
по результатам строится осциллограмма
- на вход АЦП подаем синусоиду амплитудой 1000 дискрет с частотой 100Гц, на осциллограмме имеем синусоиду амплитудой 999 дискрет
- на вход АЦП подаем синусоиду амплитудой 1000 дискрет с частотой 250Гц, на осциллограмме имеем синусоиду амплитудой 707 дискрет
- на вход АЦП подаем синусоиду амплитудой 1000 дискрет с частотой 400Гц, на осциллограмме имеем синусоиду амплитудой где-то 20 дискрет
- на вход АЦП подаем синусоиду амплитудой 1000 дискрет с частотой 900Гц, на осциллограмме имеем синусоиду амплитудой 999 дискрет, частоту 100Гц, ничего не поделаешь теорема Котельникова
Угадай фильтр по импульсной характеристике