Ví dụ mã cho bộ lọc FIR / IIR trong VHDL?


11

Tôi đang cố gắng bắt đầu với DSP trong bảng Spartan-3 của mình. Tôi đã tạo ra một bảng AC97 với một con chip từ một bo mạch chủ cũ và cho đến nay tôi đã có nó để làm ADC, nhân các mẫu cho một số <1 (giảm âm lượng) và sau đó là DAC.

Bây giờ tôi muốn thực hiện một số nội dung DSP cơ bản, như bộ lọc thông thấp, thông cao, v.v. Nhưng tôi thực sự bối rối về biểu diễn số (số nguyên? Điểm cố định? Q0.15? Tràn hoặc bão hòa?).

Tôi chỉ muốn một số mã ví dụ của một bộ lọc đơn giản thực tế để giúp tôi bắt đầu. Không có hiệu quả cao, nhanh chóng, hoặc bất cứ điều gì như thế. Chỉ là bộ lọc lý thuyết được thực hiện trong VHDL.

Tôi đã tìm kiếm nhưng tôi chỉ tìm thấy các công thức lý thuyết - tôi hiểu điều đó, điều tôi không hiểu là làm thế nào để xử lý các mẫu âm thanh 16 bit, 48KHz đã ký mà tôi nhận được từ ADC. Tôi đã sử dụng các thư viện này: http://www.vhdl.org/fphdl/ . Nếu tôi nhân các mẫu của mình với 0,5, 0,25, v.v., tôi có thể nghe thấy sự khác biệt. Nhưng một bộ lọc lớn hơn mang lại cho tôi tiếng ồn.

Cảm ơn.


2
Mặc dù tôi chỉ sử dụng bất cứ thứ gì bạn có trong tay để học các công cụ, tôi muốn chỉ ra rằng thực hiện các bộ lọc âm thanh trong một đồ họa không phải là một cách rất hiệu quả hoặc tiết kiệm chi phí để làm việc đó. Vì vậy, nếu bạn làm một dự án thực tế thì tôi khuyên bạn nên sử dụng DSP chi phí thấp thay thế. Ngoại lệ: Khi bạn đang thực hiện một số lượng kênh âm thanh vô duyên cùng một lúc hoặc bạn đang thực hiện FIR với số lượng vòi vô lý.

Câu trả lời:


8

Có vẻ như bạn cần phải tìm ra các khía cạnh DSP trước, sau đó thực hiện triển khai trong FPGA.

  • Sắp xếp DSP theo C, Matlab, Excel hoặc bất kỳ nơi nào khác
  • Hãy thử và suy nghĩ làm thế nào bạn sẽ chuyển những gì bạn đã học được từ đó vào vùng đất đồ họa
  • Khám phá bạn đã đưa ra một số giả định về việc triển khai không hoạt động tốt (ví dụ như sử dụng dấu phẩy động)
  • Quay lại và cập nhật công cụ DSP ngoại tuyến của bạn để tính đến điều này.
  • Lặp lại n lần :)

Về các loại dữ liệu, bạn có thể sử dụng số nguyên tốt.

Dưới đây là một số mã mẫu để giúp bạn đi. Lưu ý rằng nó thiếu rất nhiều vấn đề trong thế giới thực (ví dụ: đặt lại, quản lý tràn) - nhưng hy vọng đó là hướng dẫn:

library ieee;
use ieee.std_logic_1164.all;
entity simple_fir is
    generic (taps : integer_vector); 
    port (
        clk      : in  std_logic;
        sample   : in  integer;
        filtered : out integer := 0);
end entity simple_fir;
----------------------------------------------------------------------------------------------------------------------------------
architecture a1 of simple_fir is
begin  -- architecture a1
    process (clk) is
        variable delay_line : integer_vector(0 to taps'length-1) := (others => 0);
        variable sum : integer;
    begin  -- process
        if rising_edge(clk) then  -- rising clock edge
            delay_line := sample & delay_line(0 to taps'length-2);
            sum := 0;
            for i in 0 to taps'length-1 loop
                sum := sum + delay_line(i)*taps(taps'high-i);
            end loop;
            filtered <= sum;
        end if;
    end process;
end architecture a1;
----------------------------------------------------------------------------------------------------------------------------------
-- testbench
----------------------------------------------------------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
entity tb_simple_fir is
end entity tb_simple_fir;
architecture test of tb_simple_fir is
    -- component generics
    constant lp_taps : integer_vector := ( 1, 1, 1, 1, 1);
    constant hp_taps : integer_vector := (-1, 0, 1);

    constant samples : integer_vector := (0,0,0,0,1,1,1,1,1);

    signal sample   : integer;
    signal filtered : integer;
    signal Clk : std_logic := '1';
    signal finished : std_logic;
begin  -- architecture test
    DUT: entity work.simple_fir
        generic map (taps => lp_taps)  -- try other taps in here
        port map (
            clk      => clk,
            sample   => sample,
            filtered => filtered);

    -- waveform generation
    WaveGen_Proc: process
    begin
        finished <= '0';
        for i in samples'range loop
            sample <= samples(i);
            wait until rising_edge(clk);
        end loop;
        -- allow pipeline to empty - input will stay constant
        for i in 0 to 5 loop
            wait until rising_edge(clk);
        end loop;
        finished <= '1';
        report (time'image(now) & " Finished");
        wait;
    end process WaveGen_Proc;

    -- clock generation
    Clk <= not Clk after 10 ns when finished /= '1' else '0';
end architecture test;

Cảm ơn câu trả lời của bạn. Đó là ít nhiều những gì tôi đã làm nhưng tôi có một số vấn đề với đại diện số. ADC của tôi mang lại cho tôi các giá trị trong khoảng từ -32k đến + 32k (ký 16 bit). Tôi cũng có vấn đề về hằng số bộ lọc - làm thế nào để thể hiện điều đó? Và kết quả của phép nhân giữa mẫu và hằng số? Đó là điều làm tôi bối rối nhất.
hjf

@hjf - tất cả chỉ là số nguyên. Miễn là mọi thứ vẫn trong vòng 32 bit, bạn vẫn ổn. NẾU bạn cần chiều rộng lớn hơn mức đó, bạn có thể sử dụng các vectơ chưa ký hoặc đã ký rộng như bạn muốn. Hoặc sử dụng các loại fixed_point từ VHDL2008 (xem tại đây: vhdl.org/fphdl )
Martin Thompson

5

Bộ lọc FIR thông thấp đơn giản nhất bạn có thể thử là y (n) = x (n) + x (n-1). Bạn có thể thực hiện điều này khá dễ dàng trong VHDL. Dưới đây là sơ đồ khối rất đơn giản của phần cứng bạn muốn thực hiện.

Sơ đồ khối cho bộ lọc thông thấp đơn giản

Theo công thức, bạn cần các mẫu ADC hiện tại và trước đó để có được đầu ra thích hợp. Những gì bạn nên làm là chốt các mẫu ADC đến trên cạnh rơi của đồng hồ và thực hiện các phép tính thích hợp trên cạnh tăng để có được đầu ra thích hợp. Vì bạn đang cộng hai giá trị 16 bit lại với nhau, có thể bạn sẽ kết thúc bằng câu trả lời 17 bit. Bạn nên lưu trữ đầu vào vào các thanh ghi 17 bit và sử dụng bộ cộng 17 bit. Tuy nhiên, đầu ra của bạn sẽ là 16 bit thấp hơn của câu trả lời. Mã có thể trông giống như thế này nhưng tôi không thể đảm bảo rằng nó sẽ hoạt động hoàn toàn vì tôi đã không kiểm tra nó, chứ chưa nói đến việc tổng hợp nó.

IEEE.numeric_std.all;
...
    signal x_prev, x_curr, y_n: signed(16 downto 0);
    signal filter_out: std_logic_vector(15 downto 0);
...
process (clk) is
begin
    if falling_edge(clk) then
        --Latch Data
        x_prev <= x_curr;
        x_curr <= signed('0' & ADC_output); --since ADC is 16 bits
    end if;
end process;

process (clk) is
begin
    if rising_edge(clk) then
        --Calculate y(n)
        y_n <= x_curr + x_prev;
    end if;
end process;

filter_out <= std_logic_vector(y_n(15 downto 0));  --only use the lower 16 bits of answer

Như bạn có thể thấy, bạn có thể sử dụng ý tưởng chung này để thêm vào các công thức phức tạp hơn, chẳng hạn như những công thức có hệ số. Các công thức phức tạp hơn, như bộ lọc IIR, có thể yêu cầu sử dụng các biến để có được logic thuật toán chính xác. Cuối cùng, một cách dễ dàng để có được các bộ lọc có số thực là hệ số là tìm một hệ số tỷ lệ sao cho tất cả các số cuối cùng càng gần với số nguyên càng tốt. Kết quả cuối cùng của bạn sẽ phải được thu nhỏ lại theo cùng một yếu tố để có được kết quả chính xác.

Tôi hy vọng điều này có thể được sử dụng cho bạn và giúp bạn có được quả bóng lăn.

* Điều này đã được chỉnh sửa sao cho việc chốt dữ liệu và chốt đầu ra nằm trong các quy trình riêng biệt. Cũng sử dụng các loại đã ký thay vì std_logic_vector. Tôi giả sử đầu vào ADC của bạn sẽ là tín hiệu std_logic_vector.


2
Các quá trình kích hoạt cả hai cạnh (như bạn đã mô tả) rất khó có thể tổng hợp
Martin Thompson

@Martin Tôi giả sử bạn biết nhiều về FPGA hơn tôi, nhưng tôi đã chốt dữ liệu đến ở cạnh xuống và chốt đầu ra ở cạnh tăng cho một bài tập lớp nên tôi nghĩ điều này sẽ hiệu quả. Bạn có thể giải thích tại sao các quá trình như vậy không hoạt động?
dhsieh2

3
Nó sẽ hoạt động tốt trong một trình giả lập. Các bộ tổng hợp sẽ làm nghẹt thở mặc dù (theo kinh nghiệm của tôi) vì các flipflops trong thiết bị chỉ có thể đồng hồ trên một cạnh.
Martin Thompson

@ dhsieh2 Cảm ơn, đây là loại câu trả lời tôi đang tìm kiếm. Một câu hỏi khác, làm thế nào để tôi làm điều đó nếu tôi đang sử dụng các số đã ký (ADC của tôi cung cấp cho tôi các giá trị trong khoảng từ -32k đến + 32k).
hjf

4
@Martin Tôi luôn đồng hồ mọi thứ ở cả hai cạnh đồng hồ trong Xilinx FPGA, không vấn đề gì. Bạn không thể đồng hồ FF giống nhau của cả hai cạnh. Khi bạn nhìn vào đầu ra của bộ phân tích thời gian, nó thực sự cho thấy rất rõ rằng bạn đang làm các cạnh đối diện và điều chỉnh ngân sách thời gian phù hợp.

5

Một đoạn mã đơn giản khác (chỉ là ruột). Lưu ý Tôi đã không viết VHDL trực tiếp, tôi đã sử dụng MyHDL để tạo VHDL.

-- VHDL code snip
architecture MyHDL of sflt is

type t_array_taps is array(0 to 6-1) of signed (15 downto 0);
signal taps: t_array_taps;

begin

SFLT_RTL_FILTER: process (clk) is
    variable sum: integer;
begin
    if rising_edge(clk) then
        sum := to_integer(x * 5580);
        sum := to_integer(sum + (taps(0) * 5750));
        sum := to_integer(sum + (taps(1) * 6936));
        sum := to_integer(sum + (taps(2) * 6936));
        sum := to_integer(sum + (taps(3) * 5750));
        sum := to_integer(sum + (taps(4) * 5580));
        taps(0) <= x;
        for ii in 1 to 5-1 loop
            taps(ii) <= taps((ii - 1));
        end loop;
        y <= to_signed(sum, 16);
    end if;
end process SFLT_RTL_FILTER;

end architecture MyHDL;

mạch tổng hợp

Đây là một thực hiện trực tiếp. Nó sẽ yêu cầu số nhân. Việc tổng hợp mạch này, được nhắm mục tiêu cho Altera Cyclone III, đã không sử dụng bất kỳ hệ số nhân rõ ràng nào nhưng yêu cầu 350 yếu tố logic.

Đây là một bộ lọc FIR nhỏ và sẽ có phản hồi sau (không quá lớn) nhưng sẽ hữu ích như một ví dụ.

phản ứng bộ lọc

Ngoài ra, tôi có một vài ví dụ, ở đâyđây , điều đó có thể hữu ích.

Ngoài ra, câu hỏi của bạn xuất hiện để hỏi: "đại diện điểm cố định thích hợp là gì?" Thường xuyên khi thực hiện các chức năng DSP, biểu diễn điểm cố định được sử dụng, vì nó đơn giản hóa việc phân tích các bộ lọc. Như đã đề cập, điểm cố định chỉ là số nguyên khớp. Việc triển khai thực tế chỉ đơn giản là làm việc với các số nguyên nhưng biểu diễn bị đánh lừa của chúng tôi là phân số.
Các vấn đề thường phát sinh khi chuyển đổi từ số nguyên thực hiện (điểm cố định) sang / fro thiết kế dấu phẩy động.

Tôi không biết các loại điểm cố định và dấu phẩy động VHDL được hỗ trợ tốt như thế nào. Chúng sẽ hoạt động tốt trong mô phỏng nhưng tôi không biết liệu chúng có tổng hợp được với hầu hết các công cụ tổng hợp hay không. Tôi đã tạo ra một câu hỏi riêng cho việc này.


3

OpenCores có một số ví dụ DSP, IIR và FIR, bao gồm BiQuad. Bạn sẽ phải đăng ký để tải về các tập tin.

chỉnh sửa
Tôi hiểu nhận xét của Kortuk về các liên kết chết và thực sự, nếu liên kết đến OpenCores chết, câu trả lời sẽ trở nên vô dụng. Tôi khá tự tin rằng điều này sẽ không xảy ra; liên kết của tôi là một liên kết chung và nó sẽ chỉ chết nếu tên miền OpenCores hoàn chỉnh sẽ biến mất.
Tôi đã cố gắng tìm kiếm một số ví dụ tôi có thể sử dụng cho câu trả lời này nhưng tất cả chúng đều quá dài để được trình bày ở đây. Vì vậy, tôi sẽ tuân theo lời khuyên của mình để tự đăng ký trang web (tôi phải chuyển đến New York, vì thành phố quê hương của tôi không được chấp nhận) và xem mã được trình bày ở đó.


Như với tất cả mọi thứ, liên kết phá vỡ. Chúng tôi đã thảo luận trước đó rằng một liên kết tự nó không đưa ra câu trả lời. Bạn có thể mang lại một số những gì đang có và đưa ra một câu trả lời xác thịt có liên kết đó như một tài liệu tham khảo để tìm hiểu thêm?
Kortuk

@Kortuk - Tôi muốn làm điều này ngày hôm qua. Tôi đã đăng ký ngày hôm qua với opencores để có được một số chi tiết, nhưng họ cần một vài ngày để suy nghĩ liệu họ sẽ có tôi
stevenvh

rất vui khi nghe nó, tôi đã thực sự tự hỏi nếu có gì đó cản trở bạn. Mong muốn được nghe nhiều hơn về nó.
Kortuk

1

Tôi đã cố gắng triển khai các tập lệnh để thực hiện tự động các bộ lọc IIR, trong đó bạn có thể xác định liệu thiết kế có nên nhanh nhất có thể hay không (vì vậy mỗi phép nhân được thực hiện với hệ số nhân chuyên dụng) hoặc càng nhỏ càng tốt (vì vậy mỗi số nhân được sử dụng lại).

Các nguồn đã được xuất bản trên alt.source dưới dạng "triển khai thực hiện nhưng có thể tổng hợp các bộ lọc IIR trong VHDL" (bạn cũng có thể tìm thấy nó trong kho lưu trữ của google: https://groups.google.com/group/alt.source/msg/c8cf038b9b8ceeec ? dmode = nguồn )

Các bài đăng trên alt.source có định dạng "sắc nét", vì vậy bạn cần lưu tin nhắn dưới dạng văn bản và bỏ thông báo (với tiện ích "unshar") để lấy nguồn.


0

Còn cái này thì sao? https://github.com/MauererM/VIIRF

Nó thực hiện bộ lọc IIR dựa trên biquad (SOS, phần thứ hai), đảm nhiệm việc thực hiện điểm cố định. Nó cũng có các kịch bản Python để thiết kế và xác minh bộ lọc. Nó không sử dụng các cấu trúc đồ họa dành riêng cho nhà cung cấp và bạn có thể chọn sự đánh đổi giữa việc sử dụng tốc độ cao và khu vực thấp.

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.