Чи можливо створити фільтр IIR у FPGA, який працює на частоті вибірки?


9

Це питання стосується впровадження фільтра IIR у FPGA з фрагментами DSP з дуже конкретними критеріями.

Скажімо, ви робите фільтр без прямого натискання та лише з одним зворотним краном із таким рівнянням:

y[n]=y[n1]b1+x[n]

(див. зображення)

Візьмемо для прикладу фрагмент DSP48A1 від Xilinx - більшість жорстких IP DSP-фрагментів схожі.

Скажімо, у вас є аналогові дані, що надходять по 1 вибірці за годинник. Я хотів би розробити фільтр IIR, який працює синхронно за зразковим годинником.

Проблема полягає в тому, що для того, щоб запустити фрагмент DSP з максимальною швидкістю, ви не можете перемножувати І додавати в одному циклі. Ви повинні мати реєстр трубопроводів між цими компонентами.

Отже, якщо у вас є один новий зразок кожного годинника, вам потрібно буде виробляти 1 вихід на годинник. Однак вам потрібні попередні вихідні 2 годинники, перш ніж ви зможете виготовити новий у цій конструкції.

Очевидним рішенням є або обробляти дані з подвійною тактовою частотою, або вимкнути реєстр конвеєра, щоб можна було множити та додавати в одному циклі.

На жаль, якщо сказати, що ви здійснюєте вибірку з максимальною тактовою частотою повністю конвеєрного фрагмента DSP, жодне з цих рішень не можливе. Чи є інший спосіб побудувати це?

(Бонусні бали, якщо ви можете розробити фільтр IIR, який працює на половині частоти вибірки, використовуючи будь-яку кількість фрагментів DSP)

Метою було б запустити фільтр компенсації для АЦП 1 GSPS у FPGA Xilinx Artix. Їх DSP-фрагменти можуть працювати трохи більше 500 МГц при повному трубопроводі. Якщо є рішення для 1 зразка на годинник, я б спробував масштабувати рішення для 2-х проб на годину. Це все дуже просто за допомогою фільтра FIR.

Приклад фільтру IIR одного відгуку


1
Просто для уточнення, немає жодної причини, чому ви не мали б одного виходу за тактовий цикл методом трубопроводу, правда? Ви намагаєтеся мінімізувати затримку до одного тактового циклу замість двох, правда? Залежно від вашої ситуації, якщо ви використовуєте ціле число для b1, ви можете перетворити множення в гігантське додавання, включаючи x [n].
хорта

правильно - оскільки є один вхід на годинник, там повинен бути один вихід на годину. затримка теж не є проблемою. у фрагменті DSP є лише 2 вхідні суматори, а виступи зазвичай досить великі, тому ви не можете додати b1 разів за 1 тактовий цикл. головна межа полягає в тому, що на виході потрібно повернутись за 1 годин, але для вироблення потрібно 2 годин.
Marcus10110

1
Я думаю, ти все ще не розумієш, як працює трубопровід. Трубопровід потенційно збільшує затримку, але дозволяє отримати 1 вихід на кожен вхід на кожному тактовому циклі. Просто результат тепер є 2 годин після, ніж ідеальний 1 годин після. Вхід буде такою послідовністю: x [0], x [1], x [2], x [3], x [4], тоді як вихід буде на тому ж інтервалі часу y [-2], y [-1], y [0], y [1], y [2]. Ви не втрачаєте жодних зразків. Крім того, ви працюєте на FPGA, тому якщо ви хочете зробити більше роботи, ніж те, для чого призначені DSP трубопроводи, використовуйте fpga для паралельного навантаження.
хорта

Цей DSP здатний робити злиті множинні накопичення в циклі. Мені незрозуміло, хоча виведення фрагмента DSP можна підключити до власного входу за допомогою зворотного зв'язку за один цикл.
jbarlow

horta - ти маєш рацію щодо конвеєрного транспорту в цілому, але проблема полягає в тому, що в цьому випадку вкладка b1 має зворотній зв'язок - це означає, що етап в трубопроводі залежить від виходу попереднього значення. якщо для отримання наступного виходу з попереднього виходу завжди потрібно 2 тактових годин, немає можливості отримати 1 вихід на такт, незалежно від того, скільки затримок ви додали. jbarlow - ви праві, у DSP-фрагмента є опція з плавним циклом в 1 цикл. Однак у цьому випадку він не може працювати досить швидко. додавши M-регістр (див. таблицю), ви можете досягти 500 МГц. Однак тоді ви не можете перемножувати та додавати в один клік.
Marcus10110

Відповіді:


3

Я ще не працював з фільтрами IIR, але якщо вам потрібно лише обчислити задане рівняння

y[n] = y[n-1]*b1 + x[n]

один раз за цикл процесора, ви можете використовувати конвеєрний конвеєр.

В одному циклі ви робите множення, а в одному циклі потрібно робити підсумок для кожного вхідного зразка. Це означає, що ваша FPGA повинна бути здатна робити множення за один цикл при тактованні за заданою швидкістю вибірки! Тоді вам потрібно буде лише зробити множення поточного зразка ТА паралельно підсумовувати результат множення останнього зразка. Це призведе до постійного відставання обробки в 2 цикли.

Гаразд, давайте подивимось на формулу та спроектуємо конвеєр:

y[n] = y[n-1]*b1 + x[n]

Ваш код трубопроводу може виглядати приблизно так:

output <= last_output_times_b1 + last_input
last_output_times_b1 <= output * b1;
last_input <= input

Зверніть увагу, що всі три команди потрібно виконувати паралельно, і тому "вихід" у другому рядку використовує вихід з останнього тактового циклу!

Я не дуже працював з Verilog, тому синтаксис цього коду, можливо, неправильний (наприклад, відсутня бітова ширина вхідних / вихідних сигналів; синтаксис виконання для множення). Однак ви повинні отримати ідею:

module IIRFilter( clk, reset, x, b, y );
  input clk, reset, x, b;
  output y;

  reg y, t, t2;
  wire clk, reset, x, b;

  always @ (posedge clk or posedge reset)
  if (reset) begin
    y <= 0;
    t <= 0;
    t2 <= 0;
  end else begin
    y <= t + t2;
    t <= mult(y, b);
    t2 <= x
  end

endmodule

PS: Можливо, якийсь досвідчений програміст Verilog міг би відредагувати цей код і видалити цей коментар та коментар над кодом згодом. Дякую!

PPS: Якщо ваш коефіцієнт "b1" є фіксованою константою, ви, можливо, зможете оптимізувати конструкцію, застосувавши спеціальний множник, який бере лише один скалярний вхід і обчислює лише "b1".

Відповідь на: "На жаль, це насправді еквівалентно y [n] = y [n-2] * b1 + x [n]. Це пов'язано з етапом додаткового конвеєра." як коментар до старої версії відповіді

Так, це було дійсно правильно для наступної старої (НЕПРАВИЛЬНИЙ !!!) версії:

  always @ (posedge clk or posedge reset)
  if (reset) begin
    t <= 0;
  end else begin
    y <= t + x;
    t <= mult(y, b);
  end

Я сподіваюся, виправили цю помилку тепер, затримуючи вхідні значення, теж у другому регістрі:

  always @ (posedge clk or posedge reset)
  if (reset) begin
    y <= 0;
    t <= 0;
    t2 <= 0;
  end else begin
    y <= t + t2;
    t <= mult(y, b);
    t2 <= x
  end

Щоб переконатися, що він працює правильно цього разу, давайте подивимося, що відбувається на перших кількох циклах. Зауважте, що перші два цикли створюють більше або менше (визначене) сміття, оскільки попередніх вихідних значень (наприклад, y [-1] == ??) немає. Регістр y ініціалізується з 0, що еквівалентно припущенню y [-1] == 0.

Перший цикл (n = 0):

BEFORE: INPUT (x=x[0], b); REGISTERS (t=0, t2=0, y=0)

y <= t + t2;      == 0
t <= mult(y, b);  == y[-1] * b  = 0
t2 <= x           == x[0]

AFTERWARDS: REGISTERS (t=0, t2=x[0], y=0), OUTPUT: y[0]=0

Другий цикл (n = 1):

BEFORE: INPUT (x=x[1], b); REGISTERS (t=0, t2=x[0], y=y[0])

y <= t + t2;      ==     0  +  x[0]
t <= mult(y, b);  ==  y[0]  *  b
t2 <= x           ==  x[1]

AFTERWARDS: REGISTERS (t=y[0]*b, t2=x[1], y=x[0]), OUTPUT: y[1]=x[0]

Третій цикл (n = 2):

BEFORE: INPUT (x=x[2], b); REGISTERS (t=y[0]*b, t2=x[1], y=y[1])

y <= t + t2;      ==  y[0]*b +  x[1]
t <= mult(y, b);  ==  y[1]   *  b
t2 <= x           ==  x[2]

AFTERWARDS: REGISTERS (t=y[1]*b, t2=x[2], y=y[0]*b+x[1]), OUTPUT: y[2]=y[0]*b+x[1]

Четвертий цикл (n = 3):

BEFORE: INPUT (x=x[3], b); REGISTERS (t=y[1]*b, t2=x[2], y=y[2])

y <= t + t2;      ==  y[1]*b +  x[2]
t <= mult(y, b);  ==  y[2]   *  b
t2 <= x           ==  x[3]

AFTERWARDS: REGISTERS (t=y[2]*b, t2=x[3], y=y[1]*b+x[2]), OUTPUT: y[3]=y[1]*b+x[2]

Ми можемо бачити, що починаючи з cylce n = 2, ми отримуємо такий вихід:

y[2]=y[0]*b+x[1]
y[3]=y[1]*b+x[2]

що еквівалентно

y[n]=y[n-2]*b + x[n-1]
y[n]=y[n-1-l]*b1 + x[n-l],  where l = 1
y[n+l]=y[n-1]*b1 + x[n],  where l = 1

Як було сказано вище, ми вводимо додаткове відставання l = 1 циклів. Це означає, що ваш вихід y [n] затримується на відставання l = 1. Це означає, що вихідні дані еквівалентні, але затримуються на один "індекс". Для того, щоб бути більш зрозумілими: затримка вихідних даних складе 2 цикли, оскільки потрібен один (звичайний) тактовий цикл, а для проміжного етапу додається 1 додатковий (відставання l = 1) тактовий цикл.

Ось ескіз для графічного зображення способів потоку даних:

ескіз потоку даних

PS: Дякую, що уважно ознайомився з моїм кодом. Тож я і чомусь навчився! ;-) Повідомте мене, чи правильна ця версія чи ви бачите якісь проблеми.


Хороша робота! На жаль, y [n] = y [n-2] * b + x [n-1] насправді функціонально не еквівалентний y [n] = y [n-1] * b + x [n] із затримкою. Форма функції передачі IIR насправді виглядає приблизно так: y [n] = x [n] * b0 + x [n-1] * b1 - y [n-1] * a1 - y [n-2] * a2 і так далі. Форма встановлює b0 і a1 до 0, а замість цього використовує b1 і a2. Однак ця трансформація насправді створює зовсім інший фільтр. Якби був спосіб обчислити фільтр з першим знаменником (a1), встановленим на нуль, однак обидва ваші рішення спрацювали б чудово.
Маркус10110

Ну, вам потрібно правильно зрозуміти проблему "запровадженого відставання". Наприклад, фільтр "обробка потоку даних" повинен просто передати свій вхід, оскільки y [n] = x [n] працював би правильно, якщо він видає y [n] = x [n-1] як вихід. Вихід просто затримується на 1 цикл (наприклад, індекс виведення зміщений на фіксоване значення відносно всіх вхідних індексів)! У нашому прикладі це означає, що ваша функція має y[n+l] = y[n-1] * b + x[n]фіксоване значення для відставання, lяке можна переписати на, y[n] = y[n-1-l] * b + x[n-l]а для l = 1 це y[n] = y[n-2] * b + x[n-1].
SDwarfs

Для вашого складнішого фільтра IIR вам потрібно зробити те ж саме: y[n+l] = x[n] * b0 + x[n-1] * b1 - y[n-1] * a1 - y[n-2] * a2=> y[n] = x[n-l]*b0 + x[n-1-l] * b1 - y[n-1-l] * a1 - y[n-2-l]*a2. Припускаючи, що ви можете виконати всі три множення паралельно (1. етап / 1 цикл), і вам потрібно зробити це, щоб додати продукти разом, вам потрібно 2 цикли (1 цикл: додавання / підсумки перших двох результатів продукту, 1 цикл: додавання / підряд результат цих двох допоміжних / допоміжних), вам знадобляться два додаткові цикли. Тож l = (3-1) = 2 дає вам y[n]=x[n-2]*b0+x[n-1-2]*b1-y[n-1-2]*a1-y[n-2-2]*a2=>y[n]=x[n-2]*b0+x[n-3]*b1-y[n-3]*a1-y[n-4]*a2
SDwarfs

Звичайно, для цього ваша FPGA повинна вміти робити паралельно: 4 множення та 3 додавання / віднімання. Значить, вам потрібні ресурси для 4 множників і 3 суматорів.
SDwarfs

0

Так, ви можете тактирувати частоту вибірки.

Рішення цього питання полягає в маніпулюванні вихідним виразом, щоб можна було вставити регістри конвеєра, зберігаючи потрібну послідовність виводу.

Дано: y [n] = y [n-1] * b1 + x [n];

цим можна маніпулювати: y [n] = y [n-2] * b1 * b1 + x [n-1] * b1 + x [n].

Для перевірки цієї ж послідовності розглянемо, що відбувається з першими кількома зразками x [0], x [1], x [2] тощо, де до x [0] всі x, y вибірки дорівнювали нулю.

Для початкового виразу послідовність:

y = x[0],

x[1] +x[0]*b1,

x[2] +x[1]*b1 +x[0]*b1*b1,

x[3] +x[2]*b1 +x[1]*b1*b1 +x[0]*b1*b1*b1, ...

Зрозуміло, що необхідно, щоб b1 <1, інакше це буде рости без зв’язку.

Тепер розглянемо маніпульований вираз:

y = x[0],

x[0]*b1 +x[1],

x[0]*b1*b1 +x[1]*b1 +x[2],

x[0]*b1*b1*b1 +x[1]*b1*b1 +x[2]*b1 +x[3], ...

Це та сама послідовність.

Апаратне рішення для примітивів бібліотеки Xilinx потребує двох DSP48E в каскаді. Дивіться на малюнку 1-1 у UG193 v3.6 для портів та імен регістрів нижче. Перший примітив - це множення на b1 і додавання пізніше одного годинника; другий множимо на b1 * b1 і додаємо один годинник пізніше. Для цієї логіки існує 4-годинна затримка трубопроводу.

- DSP48E №1

a_port1: = b1; - постійний коефіцієнт, встановлений AREG = 1

b_port1: = x; - встановити атрибут BREG = 1

c_port1: = x; - встановити CREG = 1

- внутрішній для DSP48E №1

reg_a1 <= a_port1;

reg_b1 <= b_port1;

reg_c1 ​​<= c_port1;

reg_m1 <= reg_a1 * reg_b1;

reg_p1 <= reg_m1 + reg_c1; - вихід 1-го DSP48E

- кінець DSP48E №1

- DSP48E №2

a_port2: = reg_p2; - встановити атрибут AREG = 0

                -- this means the output of register reg_p2

                -- directly feeds back to the multiplier

b_port2: = b1 * b1; - константа, встановити BREG = 1

c_port2: = reg_p1; - встановити CREG = 1

- внутрішній для DSP48E №2

reg_b2 <= b_port2;

reg_c2 <= c_port2;

reg_m2 <= a_port2 * reg_b2;

reg_p2 <= reg_m2 + reg_c2;

- кінець DSP48E №2

Послідовність у reg_p1:

x [0],

x [1] + x [0] * b1,

x [2] + x [1] * b1,

x [3] + x [2] * b1,

тощо.

Послідовність у reg_p2 - бажаний результат. Внутрішній до другого DSP48E, регістр reg_m2 має послідовність:

x [0] * b1 * b1,

x [1] * b1 * b1 + x [0] * b1 * b1 * b1,

x [2] * b1 * b1 + x [1] * b1 * b1 * b1 + x [0] * b1 * b1 * b1 * b1

Цьому результату є приємна елегантність. Очевидно, що DSP48E не множиться і не додається в один і той же годинник, але для цього потрібне рівняння різниці. Маніпульоване рівняння різниці дозволяє нам терпіти регістри M і P в DSP48E і тактові машини на повній швидкості.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.