Apakah mungkin membuat filter IIR dalam FPGA yang clock pada frekuensi sampel?

9

Pertanyaan ini adalah tentang menerapkan filter IIR dalam FPGA dengan irisan DSP, dengan kriteria yang sangat spesifik.

Katakanlah Anda membuat filter tanpa ketukan ke depan dan hanya 1 ketuk terbalik, dengan persamaan ini:

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

(lihat gambar)

Ambil irisan DSP48A1 dari Xilinx sebagai contoh - sebagian besar irisan IP DSP serupa.

Katakanlah Anda memiliki data analog yang masuk pada 1 sampel per jam. Saya ingin merancang filter IIR yang berjalan secara sinkron pada jam sampel.

Masalahnya adalah bahwa untuk menjalankan slice DSP pada tingkat maksimum, Anda tidak dapat melipatgandakan DAN menambahkan pada siklus yang sama. Anda harus memiliki register pipa antara komponen-komponen ini.

Jadi, jika Anda memiliki 1 sampel baru setiap jam, Anda harus menghasilkan 1 output per jam. Namun, Anda memerlukan jam 2 keluaran sebelumnya sebelum Anda dapat menghasilkan yang baru dalam desain ini.

Solusi yang jelas adalah baik memproses data pada laju jam ganda, atau menonaktifkan register pipa sehingga Anda dapat mengalikan dan menambahkan dalam siklus yang sama.

Sayangnya, jika Anda mengambil sampel pada kecepatan clock maksimum dari slice DSP yang sepenuhnya disalurkan melalui pipa, tidak satu pun dari solusi itu yang mungkin. Apakah ada cara lain untuk membangun ini?

(Poin bonus jika Anda dapat merancang filter IIR yang beroperasi pada setengah dari laju sampel, menggunakan sejumlah irisan DSP)

Tujuannya adalah menjalankan filter kompensasi untuk 1 GSPS ADC dalam Xilinx Artix FPGA. Irisan DSP mereka dapat berjalan lebih dari 500 MHz saat disalin sepenuhnya. Jika ada solusi untuk 1 sampel per jam, saya ingin mencoba dan skala solusi untuk 2 sampel per jam. Ini semua sangat mudah dengan filter FIR.

umpan balik tunggal contoh filter IIR

Marcus10110
sumber
1
Hanya untuk memperjelas, tidak ada alasan mengapa Anda tidak akan memiliki satu output per siklus jam dengan metode pipa, kan? Anda mencoba meminimalkan latensi hingga satu siklus clock alih-alih dua, kan? Bergantung pada situasi Anda, jika Anda menggunakan integer untuk b1, maka Anda dapat mengonversi multiply menjadi tambah raksasa termasuk x [n].
horta
benar - karena ada satu input per jam, perlu ada satu output per jam. latensi juga bukan masalah. irisan DSP hanya memiliki 2 input adder, dan keran biasanya angka yang cukup besar, sehingga Anda tidak dapat menambahkan b1 kali dalam 1 siklus clock. batas utama adalah bahwa output perlu diumpankan kembali dalam 1 jam, tetapi dibutuhkan 2 jam untuk menghasilkan.
Marcus10110
1
Saya pikir Anda masih salah paham tentang cara kerja saluran pipa. Saluran pipa berpotensi meningkatkan latensi, tetapi memungkinkan Anda untuk mendapatkan 1 output untuk setiap input pada setiap siklus clock. Hanya saja hasilnya sekarang 2 jam setelah daripada 1 jam ideal setelah. Input akan menjadi urutan seperti ini: x [0], x [1], x [2], x [3], x [4] sedangkan output akan berada pada interval waktu yang sama y [-2], y [-1], y [0], y [1], y [2]. Anda tidak kehilangan sampel apa pun. Selain itu, Anda menggunakan FPGA, jadi jika Anda ingin menyelesaikan lebih banyak pekerjaan daripada apa yang dirancang untuk pipa DSP, gunakan fpga untuk memaralelkan beban kerja.
horta
DSP itu mampu melakukan penggandaan penggandaan yang terkumpul dalam suatu siklus. Tidak jelas bagi saya apakah output dari slice DSP dapat dihubungkan ke inputnya sendiri dengan umpan balik dalam satu siklus.
jbarlow
horta - Anda benar tentang pipelining secara umum, tetapi masalahnya adalah bahwa tab b1 dalam hal ini memiliki umpan balik - yang berarti bahwa tahapan dalam pipa tergantung pada output dari nilai sebelumnya. jika selalu membutuhkan 2 jam untuk menghasilkan output berikutnya dari output sebelumnya, tidak ada cara untuk menghasilkan 1 output per jam, terlepas dari berapa banyak latensi yang Anda tambahkan. jbarlow - Anda benar, slice DSP memiliki opsi 1 cycle menyatu. Namun itu tidak dapat berjalan cukup cepat dalam hal ini. dengan menambahkan register M (lihat lembar data) Anda dapat mencapai 500 MHz. Namun, Anda tidak dapat melipatgandakan dan menambahkan clk yang sama.
Marcus10110

Jawaban:

3

Saya belum bekerja dengan filter IIR, tetapi jika Anda hanya perlu menghitung persamaan yang diberikan

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

sekali per siklus CPU, Anda bisa menggunakan pipelining.

Dalam satu siklus Anda melakukan perkalian dan dalam satu siklus Anda perlu melakukan penjumlahan untuk setiap sampel input. Itu berarti FPGA Anda harus dapat melakukan perkalian dalam satu siklus ketika clock pada laju sampel yang diberikan! Maka Anda hanya perlu melakukan penggandaan sampel saat ini DAN penjumlahan hasil perkalian sampel terakhir secara paralel. Ini akan menyebabkan kelambatan pemrosesan konstan 2 siklus.

Ok, mari kita lihat rumus dan desain pipa:

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

Kode pipa Anda bisa terlihat seperti ini:

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

Perhatikan bahwa ketiga perintah harus dijalankan secara paralel dan "output" pada baris kedua karenanya menggunakan output dari siklus clock terakhir!

Saya tidak bekerja terlalu banyak dengan Verilog, jadi sintaksis kode ini kemungkinan besar salah (mis. Sinyal input / output bit lebar yang hilang; sintaksis eksekusi untuk perkalian). Namun Anda harus mendapatkan ide:

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: Mungkin beberapa programmer Verilog yang berpengalaman bisa mengedit kode ini dan menghapus komentar ini dan komentar di atas kode sesudahnya. Terima kasih!

PPS: Jika faktor "b1" Anda adalah konstanta tetap, Anda mungkin dapat mengoptimalkan desain dengan menerapkan pengganda khusus yang hanya membutuhkan satu input skalar dan hanya menghitung "kali b1".

Tanggapan untuk: "Sayangnya, ini sebenarnya setara dengan y [n] = y [n-2] * b1 + x [n]. Ini karena tahap pipa tambahan." sebagai komentar untuk versi jawaban lama

Ya, itu sebenarnya tepat untuk versi lama (INCORRECT !!!) berikut:

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

Mudah-mudahan saya memperbaiki bug ini sekarang dengan menunda nilai input, juga di register kedua:

  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

Untuk memastikan itu berfungsi dengan benar kali ini mari kita lihat apa yang terjadi pada beberapa siklus pertama. Perhatikan bahwa 2 siklus pertama menghasilkan lebih banyak atau lebih sedikit (didefinisikan) sampah, karena tidak ada nilai output sebelumnya (misalnya y [-1] == ??) tersedia. Register y diinisialisasi dengan 0, yang setara dengan asumsi y [-1] == 0.

Siklus Pertama (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

Siklus Kedua (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]

Siklus Ketiga (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]

Siklus Keempat (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]

Kita dapat melihat, bahwa diawali dengan silinder n = 2 kita mendapatkan output berikut:

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

yang setara dengan

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

Seperti disebutkan di atas kami memperkenalkan jeda tambahan l = 1 siklus. Itu berarti bahwa output Anda y [n] tertunda oleh lag l = 1. Itu berarti data output setara tetapi tertunda oleh satu "indeks". Agar lebih jelas: Data output tertunda menjadi 2 siklus, karena satu siklus clock (normal) diperlukan dan 1 siklus clock tambahan (lag l = 1) ditambahkan untuk tahap perantara.

Berikut ini adalah sketsa untuk menggambarkan secara grafis bagaimana data mengalir:

sketsa aliran data

PS: Terima kasih sudah memperhatikan kode saya. Jadi saya belajar sesuatu juga! ;-) Beritahu saya jika versi ini benar atau jika Anda melihat ada masalah lagi.

SDwarfs
sumber
Kerja bagus! Sayangnya, y [n] = y [n-2] * b + x [n-1] sebenarnya tidak secara fungsional setara dengan y [n] = y [n-1] * b + x [n] dengan latensi. Bentuk fungsi transfer IIR sebenarnya terlihat seperti ini: y [n] = x [n] * b0 + x [n-1] * b1 - y [n-1] * a1 - y [n-2] * a2 dan seterusnya. Formulir Anda menyetel b0 dan a1 ke 0, dan sebagai gantinya menggunakan b1 dan a2. Namun, transformasi itu sebenarnya menghasilkan filter yang sangat berbeda. Jika ada cara untuk menghitung filter dengan penyebut pertama (a1) yang disetel ke nol, kedua solusi Anda akan bekerja dengan sempurna.
Marcus10110
Nah, Anda perlu memahami masalah "lag yang diperkenalkan" dengan benar. Sebagai contoh, filter "pemrosesan aliran data" harus meneruskan inputnya karena y [n] = x [n] akan bekerja dengan benar jika menghasilkan y [n] = x [n-1] sebagai output. Output hanya tertunda oleh 1 siklus (misalnya indeks output diimbangi oleh nilai tetap relatif terhadap semua indeks input)! Dalam contoh kami ini berarti fungsi Anda y[n+l] = y[n-1] * b + x[n]dengan nilai tetap untuk lag lyang dapat ditulis ulang ke y[n] = y[n-1-l] * b + x[n-l]dan untuk l = 1 ini y[n] = y[n-2] * b + x[n-1].
SDwarfs
Untuk filter IIR Anda yang lebih kompleks, Anda perlu melakukan hal yang sama: 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. Dengan asumsi Anda dapat melakukan semua tiga perkalian secara paralel (1. tahap / 1 siklus) dan perlu melakukan untuk menambahkan produk bersama-sama Anda membutuhkan 2 siklus (1 siklus: menambah / sub pertama dua hasil produk, 1 siklus: menambah / sub hasil dari kedua add / subs), Anda akan membutuhkan 2 siklus tambahan. Jadi l = (3-1) = 2 memberi Anda 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
Tentu saja agar ini berfungsi, FPGA Anda harus dapat dilakukan secara paralel: 4 perkalian dan 3 tambahan / pengurangan. Berarti Anda membutuhkan sumber daya untuk 4 pengganda dan 3 penambah.
SDwarfs
0

Ya, Anda dapat clock pada frekuensi sampel.

Solusi untuk masalah ini adalah memanipulasi ekspresi asli sehingga register pipa dapat dimasukkan, sambil mempertahankan urutan output yang diinginkan.

Diberikan: y [n] = y [n-1] * b1 + x [n];

ini dapat dimanipulasi menjadi: y [n] = y [n-2] * b1 * b1 + x [n-1] * b1 + x [n].

Untuk memverifikasi ini adalah urutan yang sama mempertimbangkan apa yang terjadi pada beberapa sampel pertama x [0], x [1], x [2] dll, di mana sebelumnya x [0] semua x, sampel y adalah nol.

Untuk ekspresi asli urutannya adalah:

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, ...

Jelas bahwa perlu b1 <1, kalau tidak ini akan tumbuh tanpa terikat.

Sekarang perhatikan ungkapan yang dimanipulasi:

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], ...

Ini urutan yang sama.

Solusi perangkat keras dalam pustaka Xilinx akan membutuhkan dua DSP48E dalam cascade. Lihat gambar 1-1 di UG193 v3.6 untuk port dan daftarkan nama di bawah. Primitif pertama mengalikan dengan b1 dan menambahkan satu jam kemudian; yang kedua adalah mengalikan dengan b1 * b1 dan menambahkan satu jam kemudian. Ada latensi pipa 4 jam untuk logika ini.

- DSP48E # 1

a_port1: = b1; - koefisien konstan, atur AREG = 1

b_port1: = x; - atur atribut BREG = 1

c_port1: = x; - atur CREG = 1

- internal ke 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; - output 1 DSP48E

- akhir DSP48E # 1

- DSP48E # 2

a_port2: = reg_p2; - atur atribut AREG = 0

                -- this means the output of register reg_p2

                -- directly feeds back to the multiplier

b_port2: = b1 * b1; - konstan, atur BREG = 1

c_port2: = reg_p1; - atur CREG = 1

- internal ke DSP48E # 2

reg_b2 <= b_port2;

reg_c2 <= c_port2;

reg_m2 <= a_port2 * reg_b2;

reg_p2 <= reg_m2 + reg_c2;

- akhir DSP48E # 2

Urutan di reg_p1:

x [0],

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

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

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

dll.

Urutan di reg_p2 adalah hasil yang diinginkan. Di dalam DSP48E ke-2, register reg_m2 memiliki urutan:

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 * b1

Ada keanggunan yang bagus untuk hasil ini. Jelas DSP48E tidak menggandakan dan menambahkan dalam jam yang sama, namun itulah yang dibutuhkan persamaan perbedaan. Persamaan perbedaan yang dimanipulasi memungkinkan kita untuk mentolerir register M dan P dalam DSP48E dan clock dengan kecepatan penuh.

Dave Brown
sumber