Có thể tạo bộ lọc IIR trong một đồ họa có tần số mẫu không?


9

Câu hỏi này là về việc triển khai bộ lọc IIR trong một đồ họa với các lát DSP, với các tiêu chí rất cụ thể.

Giả sử bạn đang tạo bộ lọc không có vòi chuyển tiếp và chỉ có 1 lần nhấn ngược lại, với phương trình này:

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

(xem hình)

Lấy lát DSP48A1 từ Xilinx làm ví dụ - hầu hết các lát DSP IP cứng đều tương tự nhau.

Hãy nói rằng bạn có dữ liệu tương tự đến 1 mẫu trên mỗi đồng hồ. Tôi muốn thiết kế bộ lọc IIR chạy đồng bộ tại đồng hồ mẫu.

Vấn đề là để chạy lát DSP ở tốc độ tối đa, bạn không thể nhân VÀ thêm vào cùng một chu kỳ. Bạn phải có một thanh ghi đường ống giữa các thành phần này.

Vì vậy, nếu bạn có 1 mẫu mới mỗi đồng hồ, bạn sẽ cần sản xuất 1 đầu ra cho mỗi đồng hồ. Tuy nhiên, bạn cần 2 đồng hồ đầu ra trước đó trước khi bạn có thể sản xuất một cái mới trong thiết kế này.

Giải pháp rõ ràng là xử lý dữ liệu ở tốc độ xung nhịp kép hoặc vô hiệu hóa thanh ghi đường ống để bạn có thể nhân và thêm trong cùng một chu kỳ.

Thật không may, nếu nói rằng bạn đang lấy mẫu ở tốc độ xung nhịp tối đa của lát DSP hoàn chỉnh, thì cả hai giải pháp này đều không thể thực hiện được. Có cách nào khác để xây dựng điều này?

(Điểm thưởng nếu bạn có thể thiết kế bộ lọc IIR hoạt động ở một nửa tốc độ mẫu, sử dụng bất kỳ số lát DSP nào)

Mục tiêu sẽ là chạy bộ lọc bù cho ADC 1 GSPS trong Xilinx Artix FPGA. Các lát DSP của họ có thể chạy chỉ hơn 500 MHz khi được cung cấp đầy đủ. Nếu có một giải pháp cho 1 mẫu trên mỗi đồng hồ, tôi muốn thử và chia tỷ lệ cho 2 mẫu trên mỗi đồng hồ. Tất cả đều rất dễ dàng với bộ lọc FIR.

ví dụ bộ lọc IIR phản hồi duy nhất


1
Chỉ cần làm rõ, không có lý do tại sao bạn sẽ không có một đầu ra trên mỗi chu kỳ đồng hồ với phương pháp đường ống, phải không? Bạn đang cố gắng giảm thiểu độ trễ xuống một chu kỳ đồng hồ thay vì hai, phải không? Tùy thuộc vào tình huống của bạn, nếu bạn đang sử dụng một số nguyên cho b1, thì bạn có thể chuyển đổi bội số thành một số cộng khổng lồ bao gồm x [n].
horta

đúng - vì có một đầu vào trên mỗi đồng hồ, nên cần có một đầu ra trên mỗi đồng hồ. độ trễ cũng không phải là một vấn đề. lát DSP chỉ có 2 bộ cộng đầu vào và các vòi thường là những con số khá lớn, vì vậy bạn không thể thêm b1 lần trong 1 chu kỳ đồng hồ. giới hạn chính là đầu ra cần phải nạp lại trong 1 đồng hồ, nhưng phải mất 2 đồng hồ để sản xuất.
Marcus10110

1
Tôi nghĩ bạn vẫn đang hiểu sai về cách thức hoạt động của một đường ống. Một đường ống có khả năng tăng độ trễ, nhưng cho phép bạn nhận được 1 đầu ra cho mỗi đầu vào ở mỗi chu kỳ đồng hồ. Chỉ là kết quả bây giờ là 2 đồng hồ sau chứ không phải là 1 đồng hồ lý tưởng sau. Đầu vào sẽ là chuỗi như thế này: x [0], x [1], x [2], x [3], x [4] trong khi đầu ra sẽ ở cùng khoảng thời gian y [-2], y [-1], y [0], y [1], y [2]. Bạn sẽ không mất bất kỳ mẫu nào. Ngoài ra, bạn đang sử dụng một đồ họa, vì vậy nếu bạn muốn hoàn thành công việc nhiều hơn những gì các đường ống DSP được thiết kế cho, hãy sử dụng fpga để song song hóa khối lượng công việc.
horta

DSP đó có khả năng thực hiện tích lũy nhân lên trong một chu kỳ. Nó không rõ ràng với tôi mặc dù nếu đầu ra của một lát DSP có thể được kết nối với đầu vào của chính nó với phản hồi trong một chu kỳ.
jbarlow

nói chung - bạn nói đúng về đường ống nói chung, nhưng vấn đề là tab b1 trong trường hợp này có phản hồi - có nghĩa là một giai đoạn trong đường ống phụ thuộc vào đầu ra của giá trị trước đó. nếu luôn phải mất 2 đồng hồ để tạo đầu ra tiếp theo từ đầu ra trước đó, thì không có cách nào để tạo 1 đầu ra cho mỗi đồng hồ, bất kể bạn đã thêm bao nhiêu độ trễ. jbarlow - bạn nói đúng, lát DSP có tùy chọn hợp nhất 1 chu kỳ. Tuy nhiên, nó không thể chạy đủ nhanh trong trường hợp này. bằng cách thêm thanh ghi M (xem biểu dữ liệu), bạn có thể đạt tới 500 MHz. Tuy nhiên, sau đó bạn không thể nhân và thêm vào cùng một clk.
Marcus10110

Câu trả lời:


3

Tôi chưa làm việc với các bộ lọc IIR, nhưng nếu bạn chỉ cần tính toán phương trình đã cho

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

một lần trên mỗi chu kỳ CPU, bạn có thể sử dụng pipelining.

Trong một chu kỳ, bạn thực hiện phép nhân và trong một chu kỳ, bạn cần thực hiện phép tính tổng cho từng mẫu đầu vào. Điều đó có nghĩa là FPGA của bạn phải có khả năng thực hiện phép nhân trong một chu kỳ khi được chạy ở tốc độ mẫu đã cho! Sau đó, bạn sẽ chỉ cần thực hiện phép nhân của mẫu hiện tại VÀ tổng kết quả của phép nhân mẫu cuối cùng song song. Điều này sẽ gây ra độ trễ xử lý liên tục trong 2 chu kỳ.

Ok, chúng ta hãy xem công thức và thiết kế một đường ống dẫn:

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

Mã đường ống của bạn có thể trông như thế này:

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

Lưu ý rằng cả ba lệnh cần được thực thi song song và "đầu ra" trong dòng thứ hai do đó sử dụng đầu ra từ chu kỳ xung nhịp cuối cùng!

Tôi đã không làm việc nhiều với Verilog, vì vậy cú pháp của mã này rất có thể sai (ví dụ: thiếu độ rộng bit của tín hiệu đầu vào / đầu ra; cú pháp thực thi để nhân). Tuy nhiên, bạn nên có ý tưởng:

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: Có thể một số lập trình viên Verilog có kinh nghiệm có thể chỉnh sửa mã này và xóa nhận xét này và nhận xét phía trên mã sau đó. Cảm ơn!

PPS: Trong trường hợp hệ số "b1" của bạn là hằng số cố định, bạn có thể tối ưu hóa thiết kế bằng cách triển khai một hệ số nhân đặc biệt chỉ mất một đầu vào vô hướng và chỉ tính "lần b1".

Trả lời: "Thật không may, điều này thực sự tương đương với y [n] = y [n-2] * b1 + x [n]. Điều này là do giai đoạn đường ống phụ." như nhận xét cho phiên bản cũ của câu trả lời

Vâng, điều đó thực sự phù hợp với phiên bản cũ (INCORRECT !!!) sau đây:

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

Tôi hy vọng đã sửa lỗi này ngay bây giờ bằng cách trì hoãn các giá trị đầu vào, trong một thanh ghi thứ hai:

  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

Để đảm bảo nó hoạt động chính xác trong lần này, hãy xem điều gì xảy ra ở một vài chu kỳ đầu tiên. Lưu ý rằng 2 chu kỳ đầu tiên tạo ra nhiều hơn hoặc ít hơn (xác định) rác, vì không có giá trị đầu ra trước đó (ví dụ: y [-1] == ??) có sẵn. Thanh ghi y được khởi tạo bằng 0, tương đương với giả sử y [-1] == 0.

Chu kỳ đầu tiên (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

Chu kỳ thứ hai (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]

Chu kỳ thứ ba (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]

Chu kỳ thứ tư (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]

Chúng ta có thể thấy, bắt đầu với hình trụ n = 2, chúng ta có được đầu ra sau:

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

tương đương với

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

Như đã đề cập ở trên, chúng tôi giới thiệu độ trễ bổ sung là l = 1 chu kỳ. Điều đó có nghĩa là đầu ra của bạn y [n] bị trễ bởi độ trễ l = 1. Điều đó có nghĩa là dữ liệu đầu ra tương đương nhưng bị trì hoãn bởi một "chỉ mục". Để rõ ràng hơn: Dữ liệu đầu ra bị trễ là 2 chu kỳ, vì cần một chu kỳ đồng hồ (bình thường) và thêm 1 chu kỳ (lag l = 1) cho giai đoạn trung gian.

Dưới đây là một bản phác thảo để mô tả đồ họa cách dữ liệu chảy:

phác họa luồng dữ liệu

PS: Cảm ơn bạn đã xem kỹ mã của tôi. Vì vậy, tôi cũng đã học được điều gì đó! ;-) Hãy cho tôi biết nếu phiên bản này là chính xác hoặc nếu bạn thấy bất kỳ vấn đề nào nữa.


Công việc tốt đẹp! Thật không may, y [n] = y [n-2] * b + x [n-1] không thực sự có chức năng tương đương với y [n] = y [n-1] * b + x [n] với độ trễ. Dạng của hàm truyền IIR thực sự trông như thế này: y [n] = x [n] * b0 + x [n-1] * b1 - y [n-1] * a1 - y [n-2] * a2 và như thế. Biểu mẫu của bạn đặt b0 và a1 thành 0, và thay vào đó sử dụng b1 và a2. Tuy nhiên, biến đổi đó thực sự tạo ra một bộ lọc rất khác. Nếu có một cách để tính toán bộ lọc với mẫu số đầu tiên (a1) được đặt thành 0, cả hai giải pháp của bạn sẽ hoạt động hoàn hảo.
Marcus10110

Chà, bạn cần hiểu chính xác vấn đề "độ trễ giới thiệu". Ví dụ: bộ lọc "xử lý luồng dữ liệu" sẽ chỉ chuyển tiếp đầu vào của nó vì y [n] = x [n] sẽ hoạt động chính xác nếu nó tạo ra y [n] = x [n-1] làm đầu ra. Đầu ra chỉ bị trễ bởi 1 chu kỳ (ví dụ: chỉ số đầu ra được bù bởi một giá trị cố định so với tất cả các chỉ số đầu vào)! Trong ví dụ của chúng tôi, điều này có nghĩa là hàm của bạn y[n+l] = y[n-1] * b + x[n]có giá trị cố định cho độ trễ lcó thể được viết lại thành y[n] = y[n-1-l] * b + x[n-l]và với l = 1, đây là y[n] = y[n-2] * b + x[n-1].
SDwarfs

Đối với bộ lọc IIR phức tạp hơn của bạn, bạn sẽ cần phải làm tương tự: 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. Giả sử bạn có thể thực hiện song song cả ba phép nhân (1. giai đoạn / 1 chu kỳ) và cần thực hiện để thêm các sản phẩm lại với nhau, bạn cần 2 chu kỳ (1 chu kỳ: thêm / phụ hai kết quả sản phẩm đầu tiên, 1 chu kỳ: thêm / phụ kết quả của hai lần thêm / phụ), bạn sẽ cần thêm 2 chu kỳ. Vậy l = (3-1) = 2 mang lại cho bạn 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

Tất nhiên để làm việc này, FPGA của bạn phải có khả năng thực hiện song song: 4 phép nhân và 3 phép cộng / phép trừ. Có nghĩa là bạn cần tài nguyên cho 4 số nhân và 3 bộ cộng.
SDwarfs

0

Có, bạn có thể đồng hồ ở tần số mẫu.

Một giải pháp cho vấn đề này là thao tác biểu thức ban đầu để có thể chèn các thanh ghi đường ống, trong khi duy trì chuỗi đầu ra mong muốn.

Cho trước: y [n] = y [n-1] * b1 + x [n];

điều này có thể được thao tác thành: y [n] = y [n-2] * b1 * b1 + x [n-1] * b1 + x [n].

Để xác minh đây là trình tự tương tự, hãy xem xét những gì xảy ra với một số mẫu đầu tiên x [0], x [1], x [2], v.v., trong đó trước đó x [0] tất cả các mẫu x, y đều bằng không.

Đối với biểu thức ban đầu, chuỗi là:

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

Rõ ràng là cần thiết b1 <1, nếu không điều này sẽ phát triển mà không bị ràng buộc.

Bây giờ hãy xem xét biểu thức thao tác:

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

Đây là trình tự tương tự.

Một giải pháp phần cứng trong các nguyên thủy thư viện Xilinx sẽ cần hai DSP48E theo tầng. Tham khảo hình 1-1 trong UG193 v3.6 cho cổng và đăng ký tên bên dưới. Nguyên thủy đầu tiên được nhân với b1 và thêm một đồng hồ sau; cái thứ hai được nhân với b1 * b1 và thêm một đồng hồ sau. Có độ trễ đường ống 4 đồng hồ cho logic này.

- DSP48E # 1

a_port1: = b1; - hệ số không đổi, đặt ISG = 1

b_port1: = x; - đặt thuộc tính BREG = 1

c_port1: = x; - đặt CREG = 1

- nội bộ cho 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; - đầu ra của DSP48E thứ 1

- kết thúc DSP48E # 1

- DSP48E # 2

a_port2: = reg_p2; - đặt thuộc tính ISG = 0

                -- this means the output of register reg_p2

                -- directly feeds back to the multiplier

b_port2: = b1 * b1; - không đổi, đặt BREG = 1

c_port2: = reg_p1; - đặt CREG = 1

- nội bộ cho DSP48E # 2

reg_b2 <= b_port2;

reg_c2 <= c_port2;

reg_m2 <= a_port2 * reg_b2;

reg_p2 <= reg_m2 + reg_c2;

- kết thúc DSP48E # 2

Trình tự tại reg_p1:

x [0],

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

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

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

Vân vân.

Trình tự tại reg_p2 là kết quả mong muốn. Nội bộ cho DSP48E thứ 2, đăng ký reg_m2 có một chuỗi:

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

Có một sự thanh lịch tốt đẹp cho kết quả này. Rõ ràng DSP48E không nhân lên và thêm vào trong cùng một đồng hồ, tuy nhiên đó là những gì phương trình khác biệt đang yêu cầu. Phương trình sai khác thao tác cho phép chúng ta dung nạp các thanh ghi M và P trong DSP48E và đồng hồ ở tốc độ tối đa.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.