ตัวอย่างรหัสสำหรับตัวกรอง FIR / IIR ใน VHDL?


11

ฉันพยายามเริ่มต้นกับ DSP ในกระดาน Spartan-3 ของฉัน ฉันทำบอร์ด AC97 โดยใช้ชิปจากมาเธอร์บอร์ดเก่าและจนถึงตอนนี้ฉันได้รับมาเพื่อทำ ADC คูณตัวอย่างด้วยตัวเลข <1 (ลดระดับเสียง) จากนั้น DAC

ตอนนี้ฉันต้องการทำสิ่งพื้นฐาน DSP บางอย่างเช่นตัวกรอง low-pass, high-pass ฯลฯ แต่ฉันสับสนเกี่ยวกับการแสดงตัวเลข (จำนวนเต็ม? จำนวนคงที่จุดตรึง? Q0.15 ล้นหรืออิ่มตัว)

ผมแค่อยากโค้ดบางตัวอย่างของจริงกรองง่ายที่จะรับฉันเริ่มต้น ไม่มีประสิทธิภาพสูงเร็วหรืออะไรอย่างนั้น เพียงแค่ใช้ตัวกรองตามทฤษฎีใน VHDL

ฉันค้นหา แต่ฉันเพิ่งพบสูตรตามทฤษฎี - ฉันเข้าใจแล้วสิ่งที่ฉันไม่เข้าใจคือวิธีการประมวลผลตัวอย่างเสียงแบบ 48 บิตที่ได้รับการเซ็นชื่อแบบ 16 บิตที่ฉันได้รับจาก ADC ผมเคยใช้ห้องสมุดเหล่านี้: http://www.vhdl.org/fphdl/ ถ้าฉันคูณตัวอย่างด้วย 0.5, 0.25, ฯลฯ ฉันจะได้ยินความแตกต่าง แต่ตัวกรองที่ใหญ่ขึ้นทำให้ฉันมีเสียงรบกวน

ขอบคุณ


2
ในขณะที่ฉันทุกคนใช้สิ่งที่คุณมีในการเรียนรู้สิ่งต่าง ๆ ฉันอยากจะชี้ให้เห็นว่าการใช้ตัวกรองเสียงใน FPGA ไม่ใช่วิธีที่มีประสิทธิภาพหรือคุ้มค่าในการทำ ดังนั้นถ้าคุณทำโครงการจริงฉันขอแนะนำให้ใช้ DSP ราคาต่ำแทน ข้อยกเว้น: เมื่อคุณกำลังทำช่องสัญญาณเสียงจำนวนมากในเวลาเดียวกันหรือคุณกำลังทำ FIR ด้วยจำนวนก๊อกที่ไร้สาระ

คำตอบ:


8

ดูเหมือนว่าคุณจำเป็นต้องเข้าใจแง่มุมของ DSP ก่อนจากนั้นจึงทำการปรับใช้ใน FPGA

  • จัดเรียง DSP ใน C, Matlab, Excel หรือที่อื่น ๆ
  • ลองและคิดว่าคุณจะถ่ายโอนสิ่งที่คุณเรียนรู้จากสิ่งนั้นไปยัง FPGA-land ได้อย่างไร
  • ค้นพบว่าคุณได้ตั้งสมมติฐานเกี่ยวกับการนำไปใช้งานที่ไม่ได้ผล (เช่นการใช้จุดลอย)
  • ย้อนกลับและอัปเดตข้อมูล DSP ออฟไลน์ของคุณเพื่อพิจารณา
  • พูดซ้ำ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;

ขอบคุณสำหรับคำตอบ. นั่นเป็นสิ่งที่ฉันทำมากไปกว่านั้น แต่ฉันมีปัญหากับการแทนตัวเลข ADC ของฉันให้ค่าใน -32k ถึง + 32k (ลงนาม 16 บิต) ฉันยังมีปัญหาค่าคงที่ตัวกรอง - ฉันจะแสดงสิ่งนั้นได้อย่างไร และผลคูณระหว่างตัวอย่างกับค่าคงที่หรือไม่? นั่นคือสิ่งที่ทำให้ฉันสับสนมากที่สุด
hjf

@hjf - มันเป็นเพียงจำนวนเต็ม ตราบใดที่ทุกอย่างอยู่ภายใน 32 บิตคุณก็โอเค หากคุณต้องการความกว้างมากกว่านั้นคุณสามารถใช้เวกเตอร์ที่ไม่ได้ลงนามหรือเซ็นชื่อได้ตามที่คุณต้องการ หรือใช้ประเภท fixed_point จาก VHDL2008 (ดูที่นี่: vhdl.org/fphdl )
Martin Thompson

5

ตัวกรอง FIR low pass ที่ง่ายที่สุดที่คุณสามารถลองใช้ได้คือ y (n) = x (n) + x (n-1) คุณสามารถใช้สิ่งนี้ได้อย่างง่ายดายใน VHDL ด้านล่างเป็นไดอะแกรมบล็อกที่ง่ายมากของฮาร์ดแวร์ที่คุณต้องการใช้

บล็อกไดอะแกรมสำหรับ Simple Low Pass Filter

ตามสูตรคุณต้องการตัวอย่าง ADC ปัจจุบันและก่อนหน้าเพื่อให้ได้ผลลัพธ์ที่เหมาะสม สิ่งที่คุณควรทำคือการสลักตัวอยาง ADC ขาเขาที่ขอบตกของนาฬิกาและดําเนินการคํานวณที่เหมาะสมบนขอบที่สูงขึ้นเพื่อรับเอาตพุตที่เหมาะสม เนื่องจากคุณกำลังเพิ่มค่า 16 บิตสองค่าด้วยกันจึงเป็นไปได้ที่คุณจะได้คำตอบ 17 บิต คุณควรเก็บอินพุตไว้ในรีจิสเตอร์ 17 บิตและใช้แอดเดอร์ 17 บิต อย่างไรก็ตามผลลัพธ์ของคุณจะเป็นคำตอบที่ต่ำกว่า 16 บิต รหัสอาจมีลักษณะเช่นนี้ แต่ฉันไม่สามารถรับประกันได้ว่ามันจะทำงานได้อย่างสมบูรณ์ตั้งแต่ฉันยังไม่ได้ทดสอบเลยสังเคราะห์โดยลำพัง

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

อย่างที่คุณเห็นคุณสามารถใช้แนวคิดทั่วไปนี้เพื่อเพิ่มในสูตรที่ซับซ้อนมากขึ้นเช่นแนวคิดที่มีค่าสัมประสิทธิ์ สูตรที่ซับซ้อนมากขึ้นเช่นตัวกรอง IIR อาจต้องใช้ตัวแปรเพื่อให้ได้ตรรกะที่ถูกต้อง ในที่สุดวิธีง่ายๆในการหลีกเลี่ยงตัวกรองที่มีจำนวนจริงเป็นค่าสัมประสิทธิ์คือการหาตัวประกอบสเกลเพื่อให้ตัวเลขทั้งหมดจบลงได้ใกล้เคียงกับจำนวนทั้งหมดมากที่สุด ผลลัพธ์สุดท้ายของคุณจะต้องลดขนาดลงด้วยปัจจัยเดียวกันเพื่อให้ได้ผลลัพธ์ที่ถูกต้อง

ฉันหวังว่านี่จะเป็นประโยชน์กับคุณและช่วยให้คุณได้ลูกบอลกลิ้ง

* สิ่งนี้ได้รับการแก้ไขเพื่อให้ data latching และ output latching อยู่ในกระบวนการที่แยกจากกัน ใช้ประเภทที่เซ็นชื่อแทน std_logic_vector ฉันสมมติว่าอินพุต ADC ของคุณจะเป็นสัญญาณ std_logic_vector


2
กระบวนการที่ก่อให้เกิดการหักมุมทั้งสอง (ตามที่คุณอธิบาย) ไม่น่าจะเกิดขึ้นได้
Martin Thompson

@ มาร์ตินฉันสมมติว่าคุณรู้มากเกี่ยวกับ FPGA มากกว่าที่ฉันทำ แต่ฉันได้รับข้อมูลขาเข้าบนขอบตกและสลักเอาท์พุทบนขอบที่เพิ่มขึ้นสำหรับการกำหนดชั้นเรียนดังนั้นฉันคิดว่ามันจะทำงานได้ คุณช่วยอธิบายได้ไหมว่าทำไมกระบวนการดังกล่าวจึงไม่ทำงาน
dhsieh2

3
มันจะทำงานได้ดีในเครื่องจำลอง นักสังเคราะห์จะทำให้หายใจไม่ออก (ในประสบการณ์ของฉัน) เนื่องจาก flipflops ในอุปกรณ์สามารถนาฬิกาบนขอบเดียวเท่านั้น
Martin Thompson

@ dhsieh2 ขอบคุณนี่เป็นคำตอบที่ฉันต้องการ คำถามอื่นฉันจะทำอย่างไรถ้าฉันใช้หมายเลขที่เซ็นชื่อ (ADC ของฉันให้ค่าใน -32k ถึง + 32k)
hjf

4
@ มาร์ตินฉันนาฬิกาสิ่งต่าง ๆ ออกจากขอบนาฬิกาทั้งสองตลอดเวลาใน Xilinx FPGA ของไม่มีปัญหา คุณไม่สามารถจับเวลา FF เดียวกันกับขอบทั้งสองได้ เมื่อคุณดูผลลัพธ์ของตัววิเคราะห์เวลามันชัดเจนว่าคุณกำลังทำขอบตรงข้ามและปรับงบประมาณเวลาตามนั้น

5

อีกโค้ดขนาดสั้น (แค่ความกล้า) หมายเหตุฉันไม่ได้เขียน VHDL โดยตรงฉันใช้ MyHDL เพื่อสร้าง 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;

วงจรสังเคราะห์

นี่คือการดำเนินการโดยตรง มันจะต้องตัวคูณ การสังเคราะห์วงจรนี้ซึ่งกำหนดเป้าหมายไว้สำหรับ Altera Cyclone III ไม่ได้ใช้ตัวคูณที่ชัดเจน แต่ต้องการองค์ประกอบตรรกะ 350 รายการ

นี่คือตัวกรอง FIR ขนาดเล็กและจะมีการตอบสนองต่อไปนี้ (ไม่มากนัก) แต่ควรมีประโยชน์เป็นตัวอย่าง

การตอบสนองตัวกรอง

นอกจากนี้ฉันมีตัวอย่างที่นี่และที่นี่ที่อาจเป็นประโยชน์

นอกจากนี้คำถามของคุณดูเหมือนจะถามว่า: "การแสดงจุดคงที่ที่เหมาะสมคืออะไร" บ่อยครั้งเมื่อใช้ฟังก์ชั่น DSP จะใช้การแทนจุดคงที่เนื่องจากช่วยลดความยุ่งยากในการวิเคราะห์ตัวกรอง ตามที่ระบุไว้จุดคงที่เป็นเพียงจำนวนเต็ม arthimetic การใช้งานจริงนั้นใช้งานได้ง่ายกับจำนวนเต็ม แต่การนำเสนอที่เรารับรู้ล่วงหน้านั้นเป็นเพียงบางส่วน
ปัญหามักจะเกิดขึ้นเมื่อแปลงจากจำนวนเต็มของการใช้งาน (จุดคงที่) ไปเป็น / การออกแบบจุดลอยตัว

ฉันไม่ทราบว่ารองรับประเภทจุดคงที่และ VHDL ได้ดีแค่ไหน พวกเขาจะทำงานได้ดีในการจำลอง แต่ฉันไม่รู้ว่าพวกเขาจะสังเคราะห์ด้วยเครื่องมือสังเคราะห์ส่วนใหญ่หรือไม่ ฉันสร้างคำถามแยกต่างหาก สำหรับสิ่งนี้


3

OpenCoresมีตัวอย่าง DSP จำนวนมาก, IIR และ FIR รวมถึง BiQuad คุณจะต้องลงทะเบียนเพื่อดาวน์โหลดไฟล์

แก้ไข
ฉันเข้าใจความคิดเห็นของ Kortuk เกี่ยวกับลิงก์ที่ตายแล้วและถ้าลิงก์ไปยัง OpenCores ตายคำตอบนั้นจะไร้ประโยชน์ ฉันค่อนข้างมั่นใจว่าสิ่งนี้จะไม่เกิดขึ้น ลิงก์ของฉันเป็นโดเมนทั่วไปและจะตายก็ต่อเมื่อโดเมน OpenCores ที่สมบูรณ์จะหายไป
ฉันพยายามค้นหาตัวอย่างที่ฉันสามารถใช้สำหรับคำตอบนี้ แต่มันยาวเกินกว่าที่จะแสดงได้ที่นี่ ดังนั้นฉันจะทำตามคำแนะนำของฉันเพื่อลงทะเบียนเว็บไซต์ด้วยตัวเอง (ฉันต้องย้ายไปที่นิวยอร์กเพราะเมืองของฉันไม่ได้รับการยอมรับ) และดูรหัสที่นำเสนอที่นั่น


เช่นเดียวกับทุกสิ่งลิงค์แตก เราได้พูดคุยกันมาก่อนแล้วว่าลิงก์ของตัวมันเองนั้นไม่ได้ให้คำตอบ คุณช่วยนำสิ่งที่มีและทำคำตอบเนื้อสัตว์ที่มีการเชื่อมโยงนั้นเป็นข้อมูลอ้างอิงเพื่อเรียนรู้เพิ่มเติม
Kortuk

@Kortuk - ฉันต้องการทำเมื่อวานนี้ เมื่อวานนี้ผมลงทะเบียนกับ opencores ที่จะได้รับรายละเอียดบางอย่าง แต่พวกเขาต้องการไม่กี่วันที่จะคิดว่าถ้าพวกเขาจะมีฉัน
stevenvh

ดีใจที่ได้ยินเช่นนั้นฉันสงสัยอย่างสุจริตใจว่ามีบางสิ่งเกิดขึ้นในแบบของคุณ หวังว่าจะได้ยินเพิ่มเติมเกี่ยวกับมัน
Kortuk

1

ฉันได้ลองใช้สคริปต์สำหรับการใช้งานตัวกรอง IIR อัตโนมัติซึ่งคุณสามารถกำหนดได้ว่าการออกแบบควรเร็วที่สุดเท่าที่จะเป็นไปได้หรือไม่ (ดังนั้นการคูณแต่ละครั้งจะดำเนินการโดยใช้ตัวคูณเฉพาะ) หรือเล็กที่สุด

แหล่งที่มาได้รับการเผยแพร่ใน alt.sources เป็น "การดำเนินการตามพฤติกรรม แต่สามารถสังเคราะห์ได้ของตัวกรอง IIR ใน VHDL" (คุณยังสามารถค้นหาได้ใน google archive: https://groups.google.com/group/alt.sources/msg/c8cf038b9b8ceeec ? dmode = แหล่งที่มา )

โพสต์ไปยัง alt.sources อยู่ในรูปแบบ "แชร์" ดังนั้นคุณต้องบันทึกข้อความเป็นข้อความและเลิกแชร์ (ด้วยยูทิลิตี "unshar") เพื่อรับแหล่งที่มา


0

แล้วเรื่องนี้ล่ะ https://github.com/MauererM/VIIRF

มันใช้ biquad (ส่วน SOS ลำดับที่สอง) ตามตัวกรอง IIR ที่ดูแลการใช้งานจุดคงที่ นอกจากนี้ยังมีสคริปต์ Python สำหรับการออกแบบและตรวจสอบตัวกรอง มันไม่ได้ใช้โครงสร้าง FPGA เฉพาะของผู้ขายและคุณสามารถเลือกการแลกเปลี่ยนระหว่างความเร็วสูงและการใช้พื้นที่ต่ำ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.