Ogłoszenie

Collapse
No announcement yet.

Filtr cyfrowy - czyli jak interpolować

Collapse
Ten temat jest przyklejony.
X
X
 
  • Filtr
  • Czas
  • Pokaż
Clear All
new posts

    Filtr cyfrowy - czyli jak interpolować

    Przyszedł czas aby zacząć temat o nowym projekcie, tj. filtr cyfrowy typu FIR (filtr o skończonej odpowiedzi impulsowej, z ang. Finite Impulse Response) zaimplementowany w FPGA :)

    W sumie nie wiem od czego tu zacząć, ponieważ matematyka stojąca za cyfrowym filtrowaniem i ditheringiem nie będzie rozwijana, ale jednak trzeba coś wspomnieć aby zrozumieć działanie filtrowania częstotliwości w domenie cyfrowej. Zacznijmy jednak od tego, że zadaniem takiego filtru jest dokładnie takie same jak zwrotnicy w kolumnie głośnikowej, tj. filtrowanie danych częstotliwości i przepuszczanie innych Różnica jest jednak taka, że taki filtr pracuje w domenie cyfrowej oraz ma zazwyczaj wysoki rząd, tzw. dużą ilość tapsów bądź współczynników. Taki filtr może być każdego typu, tj. dolnoprzepustowy, górnoprzepustowy, środkowyprzepustowy czy jakikolwiek inny typ, tj. zależy to tylko i wyłącznie od użytych współczynników. W tym wypadku będę skupiać się na filtrze dolnoprzepustowym, ponieważ takim typu filtrem jest ten projekt.

    Pozwolę sobie zapożyczyć grafikę filtru typu FIR aby wyjasnić mniej więcej na jakiej zasadzie działa taki filtr:


    Mamy wejście filtru, które w naszym wypadku jest wejściem strumienia danych audio. Człon Z -1 to po prostu próbka audio opóźniona o jeden, tj. każda poprzednia próbka przed tą nowszą (od najnowszej do najstarszej). Człony Bx to współczynniki filtru, które mnożone są poprzez wcześniejsze próbki ze strumienia audio. Wszystkie wyniki tych mnożeń są potem dodawane do siebie i tak o to otrzymujemy próbkę na wyjściu układu

    Najprostszym przykładem filtru FIR jest po prostu liczenie średniej wartości ze wszystkich poprzednich próbek. Zobaczmy na powyższy obrazek i załóżmy, że wszystkie trzy człony Bx mają taką samą wartość, tj. 1/3. Zakładając, że na wejściu układu mamy próbki o wartościach 1, 2 i 3 to matematyka stojąca za filtrem wygląda następująco: 1/3 * 1 + 1/3 * 2 + 1/3 * 3 = 2 Inaczej pisząc w przypadku takich współczynników liczymy tylko średnią na bazie N próbek wejściowych. Inną nazwą takiego filtru to średnia ruchoma, tj. z ang. moving-average (https://pl.wikipedia.org/wiki/Średnia_ruchoma). Ciekawe jest to, że taki filtr już kiedyś stworzyłem, ale nie sam filtr i nie cyfrowo, ale wbudowany w przetwornik D/A, tj. temat z "NOS" DAC:

    https://diyaudio.pl/showthread.php/2...-dobrze-zrobić

    Przykładowy FIR oraz powyższy przetwornik D/A działają dokładnie tak samo, ale jeden działa w domenie cyfrowej a drugi w analogowej Taki filtr jest tzw. ekstrapolatorem rzędu pierwszego. Niestety jego zdolność filtrowania i rekonstrukcji sygnału pozostawia wiele do życzenia, więc jest to generalnie tylko ciekawostka, ale ma też swoje plusy (szczególnie przy strumieniu DSD). Myślę, że warto tutaj wspomnieć też o tym, że ekstrapolatorem rzędu zerowego jest zwyczajny przetwornik R-2R w formie NOS Poniższy obrazek przedstawia różnicę między ekstrapolatorem rzędu zerowego (NOS) a ekstrapolatorem rzędu pierwszego (liniowa interpolacja) oraz ich odpowiedzi impulsowe wraz z charakterystyką częstotliwościową:


    Naprawdę nie ma co się rozpisywać, ponieważ pisanie o filtrach FIR może i być ciekawe, ale zbędne i czasochłonne. Temat dotyczy samego projektu takiego filtru a nie tego jaka matematyka za tym wszystkim stoi, więc postaram się jeszcze po szybkości wspomnieć o interpolacji, filtrowaniu sin(x) / x i ditheringu oraz przejść do samego projektu filtru na FPGA.

    W nazwie temat wspomniałem, że zaprojektowany filtr cyfrowy jest typu interpolacyjnego, tj. takiego, który zwiększa oryginalną częstotliwość próbkowania, przykładowo 44.1 kHz, do zadanej częstotliwości, np. w moim wypadku jest to 705.6 kHz. Z prostej matematyki można wywnioskować, że filtr interpoluje 16-krotnie (44.1 kHz * 16 = 705.6 kHz). Zacznijmy jednak od tego co to jest interpolacja. Interpolacja jest to proces wyznaczenia pewnej funkcji, która przyjmuje takie same wartości w zadanej przedziale w którym są już wyznaczone pewne punkty. Sygnał audio zapisany jest jako wartość amplitudy (punkt na osi Y) w pewnych odstępach czasowych (oś X, np. 44.1 kHz). Inaczej pisząc zadaniem interpolacji jest po prostu wyznaczenie dodatkowych punktów pomiędzy istniejącymi już punktami. Taki zabieg najlepiej przedstawić na obrazku, więc zobaczmy poniżej:



    Czerwony to sygnał taki jaki mamy zapisany w pliku i zrekonstruowany na przetworniku R-2R w formie NOS (ekstrapolator rzędu zerowego). Czarny to ten sam sygnał, ale interpolowany do nieskończenie wyższej częstotliwości w celu odtworzenia większej ilości punktów oryginalnego zapisu. Inaczej pisząc taki zabieg w praktyce to po prostu rekonstrukcja sygnału wraz z filtrowaniem dolnoprzepustowym. Interpolacja w formie cyfrowej to dwa zabiegi, tj. wrzucenie próbek o zerowej amplitudzie do sygnału tak aby zwiększyć częstotliwość oraz filtrowanie dolnoprzepustowe w celu pozbycia się odbić sygnału, które powstały z powodu zwiększenia próbkowania. Na poniższych obrazkach można zobaczyć jak to w praktyce wygląda:





    W pierwszym obrazku na samej górze posiadamy sygnał oryginalny, dyskretny (czarne punkty) oraz ciągły (szara linia). Sygnału w formie ciągłej nie da się zapisać, ponieważ wymagałoby to nieskończonej ilości miejsca, więc w praktyce zapisujemy tylko sygnał w formie dyskretnej (próbkowany w równych odstępach czasu, np. 44.1 kHz). W celu odtworzenia oryginalnego sygnału musimy zwiększyć ilość próbek, które pozwolą nam zamienić sygnały dyskretny na sygnał ciągły (analogowy). W tym celu wrzucamy próbki o wartości 0 pomiędzy już istniejące próbki (drugi wykres). Taki zabieg pozwolił nam faktycznie zwiększyć próbkowania, ale niestety wprowadził też odbicia sygnału. W praktyce podczas próby odtworzenia takiego sygnału otrzymujemy sygnał powielony względem "lustra", które tworzy się na wielokrotności oryginalnej częstotliwości próbkowania (44.1 kHz). Ten problem można zobaczyć na drugim obrazku po prawej stronie gdzie widnieje wykres w domenie częstotliwościowej. W celu eliminacji tych odbić stosuje się filtrowanie dolnoprzepustowe, które w praktyce przesuwa odbicia na wyższą częstotliwość gdzie można już spokojnie filtrować filtrem analogowym z niskim rzędem. Inaczej pisząc czym większa interpolacja tym dalej przesuwane są odbicia oryginalnego sygnału.

    No i w sumie to tyle wystarczy na temat samej interpolacji, więc na szybkości przejdźmy jeszcze do filtrowania dolnoprzepustowego funkcją sin(x) / x. W przetwarzaniu sygnału używa się tylko funkcji sin(x) / x do filtrowania dolnoprzepustowego, ponieważ z matematycznego punktu widzenia jest ona idealnym filtrem dolnoprzepustowym (z ang. brick-wall). Wynika to z faktu, że odpowiedź częstotliwościowa funkcji sin(x) / x jest funkcją prostokątną:


    Oznacza to w praktyce tyle, że przepuszcza idealnie zadane częstotliwości a wszystkie poza wyznaczoną odpowiedzią częstotliwościową po prostu usuwa. Normalnie idealny filtr, ale nie ma tak łatwo Niestety funkcja sin (x) / x jest nieskończona, więc nie da się jej przedstawić w formie praktycznego filtru cyfrowego. W tym celu używa się metody tzw. okna czasowego (z ang. windowing), który pozwala nam "wygasić" funkcję po pewnym okresie, która w praktyce jest nieskończona. Przykładowo taką funkcją okna czasowego może być okno Kaisera (ja takiego użyłem do wyznaczenia współczynników mojego filtru sin (x) / x). Okno Kaisera wygląda jak na poniższym obrazku:


    Teraz zobaczmy sobie co takie okno czasowe może zdziałać z nieskończona funkcją sin (x) / x:



    Czerwony wykres - okno czasowe.
    Zielony wykres - nieskończona funkcja sin(x) / x.
    Niebieski wykres - funkcja sin(x) / x po zastosowaniu okna czasowego.

    Taki zabieg w praktyce pozwala zastosować funkcję sin(x) / x do filtrowania Oczywiście wprowadza to pewne błędy filtrowania i taki filtr nigdy nie będzie idealny jak sama funkcja sin(x) / x, ale zwiększając ilość współczynników (dyskretnie zapisanego sygnału sin(x) / x po oknie czasowym) można dążyć do tej idealnej odpowiedzi częstotliwościowej. Niestety trzeba też pamiętać o tym, że w praktyce mamy ograniczoną ilość pamięci w której możemy zapisać współczynniki, więc wprowadza to dodatkowe błędy kwantyzacji (cyt. "nieodwracalne nieliniowe odwzorowanie statyczne zmniejszające dokładność danych przez ograniczenie ich zbioru wartości").

    No i można zacząć filtrować! No prawie, ponieważ jako, że pracujemy na dyskretnych sygnałach a nie na ciągłych i do tego jeszcze mamy do czynienia z błędami kwantyzacji to jeszcze dosyć ważną rzeczą przy takim projekcie jest tzw. dithering podczas redukcji bitów wyjściowego słowa. Dlaczego redukcji? W praktyce nasz przetwornik D/A operuje przykładowo na słowach audio o długości 18 bitów. Zakładając, że nasze słowo wejściowe to 24 bitowy sygnał audio oraz sam fakt, że te słowo przechodzi filtrowanie w którym występuje mnożenie, powoduje, że zależnie od ilości bitów współczynników filtru, które mogą przykładowo mieć 32 bity, otrzymujemy wynik na poziomie 56 bitów. No dobra, ale mnożymy 24 bitową liczbę przez 32 bitową liczbę, więc jakim cudem mamy wynik na 56 bitach? Taka po prostu jest matematyka Mnożąc dwie liczby X oraz Y o długości bitów odpowiednio 24 oraz 32 wynik tego zabiegu wymaga 56 bitów do zapisu. Zresztą, nawet jakbyśmy nie musieli nic mnożyć to i tak musimy zredukować 24 bity do 18 bitów, które przyjmuje przetwornik. Taki zabieg jest wymagany, ponieważ błędy kwantyzacji sygnału wprowadzane przez redukcję bitów (np. ucięcie ostatnich bitów) są wprowadzane w sam sygnał audio. Dither to najprostsza metoda przeniesienia energii tych błędów kwantyzacji na losowy szum w sygnale audio. Inaczej pisząc specjalnie dodajemy szum do sygnału audio Najlepiej będzie to pokazać na przykładzie:

    Bez ditheringu:


    Z ditheringiem:


    Na przykładzie tego filtra zostało pokazane jak dither może pomóc przy odwzorowaniu sygnału -96 dBFS na 18 bitowym przetworniku :) Na górze też jest średnia z 10 pomiarów, więc koniec końców różnica w szumie jest żadna :)

    Dithering wymaga generatora liczb pseudolosowych, ponieważ chcemy aby nasz szum był po prostu losowy. No prawie, ponieważ nie potrzebujemy losowości w sensie nieprzewidywalności samych generowanych liczb, ale ciągu generowanych wartości, które mają rozkład jednostajnie ciągły, tj. taki dla którego wystąpienie danej liczby z danego przedziału jest tak samo prawdopodobne jak dla każdej innej z tego samego przedziału. Pewną własnością rozkładu jednostajnie ciągłego jest to, że dodanie dwóch takich losowych liczb zamienia rozkład na trójkątny, czyli taki, którego gęstość prawdopodobieństwa rozkłada się w formę trójkątną gdzie jego wierzchołek to liczby, które mają największą szansę na trafienie. Na poniższym obrazku X oraz Y to rozkład jednostajnie ciągły a Z to trójkątny:



    Rozkład trójkątny jest świetny do ditheringu audio, ponieważ zazwyczaj mając jedynkę chcemy aby dalej to była jedynka, tj. z dużym prawdopodobieństwem, ale aby nie było to równomierne. Inaczej pisząc ditheringiem można nazwać operację posiadania liczby X oraz losowania liczby Y chcąc trafić tak, aby X ~ Y, ale jednak nie chcemy znanej liczby X, ale nowej liczby Y, która ma duże prawdopodobieństwo trafić blisko X

    Źródłem takiej pseudo losowości do wygenerowania rozkładu trójkątnego w FPGA może być rejestr przesuwający z liniowym sprzężeniem zwrotnym, który także został zaimplementowany w tym projekcie. Zaimplementowany rejestr ma okres 2^47 i pracuje z zegarem MCLK, który może tykać z częstotliwością do 49.152 MHz. Z prostej matematyki wynika, że taki rejestr zanim zacznie się powtarzać z liczbami to minie ponad miesiąc ciągłej pracy, więc tym bardziej jest wystarczający do generowania pseudo losowych wartości Zresztą, po pomiarach z ditherem i bez można jasno wywnioskować, że losowość spełnia pewne wymogi i energia błędu kwantyzacji jest zamieniana na energię szum.

    No dobra, może koniec już samej teorii. Myślałem, że skrócę to wszystko dosyć znacząco omijając pewne rzeczy, ale teraz jak na to patrzę to widzę, że nawet mocno skrócony opis potrafi być rozległy w takiej tematyce.

    _____________________________________

    W każdym wypadku przejdźmy do samego projektu filtru, który został zaimplementowany w FPGA (bezpośrednio programowalna macierz bramek) w formie RTL (opisu sprzętu w formie logiki w VHDL'u). FPGA użyte w tym projekcie to Spartan-6 XC6SLX9 w obudowie TQFP-144. W innych projektach zazwyczaj używam Spartan-3 XC3S50AN, ponieważ posiadam ich jeszcze trochę i sobie cenię ich prostotę oraz wbudowany FLASH do konfiguracji. Niestety do tego projektu taki Spartan-3 jest po prostu za słaby i nie posiada odpowiednich jednostek do implementacji sensownego filtru cyfrowego. Pewnie, jakiś filtr można tam zmieścić, ale po co tworzyć następne dziadostwo, które w sumie nie wiele więcej wprowadza niż gotowe układy dostępne na rynku od lat 90. Zaprojektowany filtr implementuje zarówno interpolację jak i decymację. Matematycznie wykonuje on 16-krotną interpolację dla każdej wejściowej częstotliwości (tj. od 44.1 kHz do 768 kHz). W praktyce wygląda to tak, że sygnał 768 kHz też jest interpolowany 16-krotnie (do 12.288 MHz), ale koniec końców sygnał jest decymowany do zadanej wartości (odrzucanie próbek). Operacja interpolacji i decymacji na tych samych współczynnikach skraca się, więc w praktyce to wygląda tak, że filtr nie liczy próbek, które i tak zostaną odrzucone

    Na rynku mamy wiele układów filtrów cyfrowych takich jak SAA7220, DF1706, SM5847, itp. Wszystkie te układy służą do tego samego, tj. interpolacji sygnału audio. W praktyce niektóre są lepsze a niektóre gorsze, więc każdy będzie grał inaczej. Przykładowo SAA7220 jest chyba najlepszym przykładem kiepskiego filtru zrobionego po taniości Współczynniki 12-bitowe, słowo wejściowe maksymalnie do 16 bitów, bardzo mały akumulator (dużo błędów zaokrąglenia wyników mnożenia) oraz brak ditheringu. Ewidentnie widać, że to co stracimy w kiepskim filtrze cyfrowym na pewno nie uda nam się nadrobić przetwornikiem D/A. Filtry cyfrowe takie jak DF1706 czy SM5847 są już znacząco lepsze i to widać, ale ich kaskadowana interpolacja na różnych współczynnikach raczej do mnie nie przemawia. Filtr taki jak SM5847 interpoluje 8-krotnie w 3 fazach, tj. pierwsza faza posiada 169 współczynników (fs do 2fs), druga faza już tylko 29 współczynników (2fs do 4fs) oraz ostatnia faza z 17 współczynnikami (4fs do 8fs). Tak po prostu łatwiej było go zaprojektować, ale niekoniecznie lepiej sonicznie. Filtr cyfrowy przedstawiony w tym temacie interpoluje i decymuje na tych samych współczynnikach, więc nie ważne jaka jest częstotliwość wejściowa to zawsze te same współczynniki używane są do stworzenia wyjściowego strumienia Mój filtr ma stałą wartość polifazy oraz interpolacji, która wynosi 16. Oznacza to w sumie tyle, że każda nowa próbka audio tworzona jest na bazie 512 poprzednich próbek (8192 / 16 = 512). Sygnał powinien być perfekcyjnie zrekonstruowany na bazie poprzednich próbek.

    W danej chwili współczynniki filtru wraz z użytym oknem oraz pasmem przepustowym wyglądają następująco:


    Trzeba pamiętać, że filtr jest interpolacyjny, więc pasmo przepustowe liczy się na bazie docelowej częstotliwości.

    W praktyce nie ma co się do tych współczynników przywiązywać, ponieważ zależnie od sonicznych doświadczeń można w chwilę załadować nowe i znowu odsłuchiwać

    Poniżej parę zdjęć, które już wrzucałem w budowanie na ekranie, ale warto też pokazać je w tym temacie:

    Rekonstrukcja sygnału 20 kHz przy próbkowaniu 48 kHz:


    Inaczej mówiąc idealny sinus, czyli taki jaki powinien być.

    Warto pokazać jak wygląda sinus 10 kHz przy ekstrapolatorze rzędu zerowego (NOS) oraz przy ekstrapolatorze rzędu pierwszego (liniowa interpolacja), więc odpowiednio z mojego tematu o NOS DAC:

    10 kHz i ekstrapolator rzędu zerowego (R-2R, taki typowy NOS):


    10 kHz i ekstrapolator rzędu pierwszego (liniowa interpolacja):


    Jak widać takie przetworniki nie radzą sobie z filtrowaniem i odwzorowaniem sygnału przy 10 kHz a gdzie tam do 20 kHz. Oczywiście jest to całkowicie normalne, ponieważ taki był ich zarys działania, ale warto o tym wspomnieć.

    Tak samo odpowiedź impulsowa filtru cyfrowego z tego tematu:


    Tutaj widać lekkie "dzwonienie" od filtru cyfrowego i jest to całkowicie normalne, ponieważ pokazuje to, że filtr ma ograniczone pasmo do rekonstrukcji sygnału. Gdyby filtr był nieskończony tak jak funkcja sin(x) / x oraz jego pasmo byłoby nieskończone to bylibyśmy w stanie idealnie odwzorować prostokąt, ale po prostu nie ma takiej fizycznej możliwości a nawet jakby była to skończylibyśmy na nieprawidłowo odwzorowanym sygnale audio :)

    Filtr wygląda jak poniżej:



    Zworka ROM decyduje o tym jaki filtr załadować do głównej pamięci RAM. Dostępne są dwa filtry, tj. o liniowej fazie oraz o minimalnej fazie. Oba są rzędu 8192 i fizycznie nie da się więcej zmieścić :)

    Dodatkowo DAC na AD1865 do tego projektu:



    Przy okazji zaprojektowałem też specjalną wersję filtru pod TDA1541 oraz TDA1540:



    W tym projekcie interpolacja jest 8-krotna oraz taktowanie przetwornika wygląda trochę inaczej. Dodatkowo sam zegar CLK jest synchroniczny względem MCLK.

    Podsumowując główne cechy mojego filtru są następujące:

    - Interpoluje do 705.6 kHz bądź 768 kHz. Zawsze. Niezależnie od częstotliwości wejściowej, który może wahać się od 44.1 kHz do 768 kHz.
    - 8192 współczynników w dwóch różnych filtrach do wyboru (liniowa faza oraz minimalna faza).
    - Asynchroniczny zegar taktujący dane do przetwornika. Taki zabieg pozwala taktować PCM56, AD1865 i podobne aż do 768 kHz :)
    - Wewnętrzne tłumienie o 1 dB.
    - Jednostki mnożące to 32x35 z akumulatorem 67-bitowym, więc słowo audio jest akceptowane w pełni do 32 bitów rozdzielczości a współczynniki mają rozdzielczość 35 bitów. Błędy kwantyzacja na tym poziomie są już naprawdę minimalne.
    - Główny rdzeń pracuje przy częstotliwości 225 MHz.
    - Dithering TDPF.
    - Wyjście strumienia jest osobne dla kanału L oraz R. Posiada tez odwrócone wyjścia, więc można podłączyć przetworniki w formie różnicowej.
    - Wybór wyjściowego słowa od 16 do 24 bitów.

    Rdzeń pracuje przy takiej częstotliwości, że wymaga to dobrej znajomości budowy FPGA oraz idei przetwarzania potokowego (z ang. pipeliningu) a routowanie przy próbie spełnienia wymagań czasowych wygląda następująco:

    [code]
    PAR will use up to 4 processors
    Starting Multi-threaded Router


    Phase 1 : 11122 unrouted; REAL time: 3 secs

    Phase 2 : 8282 unrouted; REAL time: 4 secs

    Phase 3 : 2791 unrouted; REAL time: 6 secs

    Phase 4 : 2921 unrouted; (Setup:10769, Hold:1222, Component Switching Limit:0) REAL time: 7 secs

    Updating file: core.ncd with current fully routed design.

    Phase 5 : 0 unrouted; (Setup:21621, Hold:581, Component Switching Limit:0) REAL time: 13 secs

    Phase 6 : 0 unrouted; (Setup:20232, Hold:581, Component Switching Limit:0) REAL time: 15 secs

    Phase 7 : 0 unrouted; (Setup:20232, Hold:581, Component Switching Limit:0) REAL time: 20 secs

    Phase 8 : 0 unrouted; (Setup:20232, Hold:581, Component Switching Limit:0) REAL time: 20 secs

    Phase 9 : 0 unrouted; (Setup:20232, Hold:581, Component Switching Limit:0) REAL time: 20 secs

    Phase 10 : 0 unrouted; (Setup:20232, Hold:0, Component Switching Limit:0) REAL time: 20 secs

    Phase 11 : 0 unrouted; (Setup:0, Hold:0, Component Switching Limit:0) REAL time: 20 secs
    Total REAL time to Router completion: 20 secs
    Total CPU time to Router completion (all processors): 33 secs [/code]

    Inaczej mówiąc trzeba się modlić, że wartości Setup oraz Hold zostaną sprowadzone do zera

    Projekt jest jeszcze w fazie rozwoju, więc pewne rzeczy mogą się jeszcze zmienić. Na razie tyle, ale temat będzie kontynuowany
    Last edited by .3lite; 24.10.2018, 10:20.

    #2
    Co tu dużo pisać... Grubo :) czapki z głów, robi wrażenie :)

    Skomentuj


      #3
      Fajny kawałek teorii. Przypomniały mi się czasy studiów :-)

      Skomentuj


        #4
        Małe rozwinięcie projektu - współczynniki filtru zapisane są teraz na 35 bitach

        Przypomniało mi się, że Block RAM w FPGA ma jeszcze tzw. "parity bits" na których można zapisać dodatkowe 4 bity (oprócz standardowych 32 bitów). W praktyce oznacza to wielkość danych na poziomie 36 bitów. Jednostki MAC w moim projekcie mnożyły liczby 32x32 (32 bity na słowo audio oraz 32 bity na współczynnik filtru), ale jako, że są użyte tak czy siak 4 jednostki DSP48A1 to w praktyce można zrobić zwiększyć mnożnik do 35x35 na tych samych jednostkach (DSP48A1 mnoży 18-bitowe liczby z dodatkowym bitem znaku, więc stworzenie mnożnika 32x32 lub 35x35 tak czy siak wymaga 4 takich jednostek). No to zmieniłem moje jednostki MAC w celu wykonywania mnożeń 32x35 oraz zwiększyłem pamięć ROM współczynników do 35 bitów i modliłem się o spełnienie wymagań:

        [code]
        HDL Synthesis Report

        Macro Statistics
        # RAMs : 4
        16x8-bit single-port Read Only RAM : 1
        512x32-bit dual-port RAM : 2
        8192x35-bit dual-port Read Only RAM : 1
        # Multipliers : 4
        35x32-bit multiplier : 4

        ================================================== =======================

        Overall effort level (-ol): High
        Router effort level (-rl): High

        PAR will use up to 4 processors
        Starting Multi-threaded Router


        Phase 1 : 11274 unrouted; REAL time: 3 secs

        Phase 2 : 8393 unrouted; REAL time: 4 secs

        Phase 3 : 2882 unrouted; REAL time: 6 secs

        Phase 4 : 3069 unrouted; (Setup:16481, Hold:783, Component Switching Limit:0) REAL time: 7 secs

        Updating file: core.ncd with current fully routed design.

        Phase 5 : 0 unrouted; (Setup:26243, Hold:176, Component Switching Limit:0) REAL time: 17 secs

        Phase 6 : 0 unrouted; (Setup:24205, Hold:176, Component Switching Limit:0) REAL time: 20 secs

        Phase 7 : 0 unrouted; (Setup:24205, Hold:176, Component Switching Limit:0) REAL time: 22 secs

        Phase 8 : 0 unrouted; (Setup:24205, Hold:176, Component Switching Limit:0) REAL time: 22 secs

        Phase 9 : 0 unrouted; (Setup:24205, Hold:176, Component Switching Limit:0) REAL time: 22 secs

        Phase 10 : 0 unrouted; (Setup:24205, Hold:0, Component Switching Limit:0) REAL time: 22 secs

        Phase 11 : 0 unrouted; (Setup:0, Hold:0, Component Switching Limit:0) REAL time: 23 secs
        Total REAL time to Router completion: 23 secs
        Total CPU time to Router completion (all processors): 32 secs
        [/code]

        [code]
        ================================================== ==============================
        Timing constraint: PERIOD analysis for net "CLK_225MHz_int" derived from NET "CLK_50MHz_IBUFG" PERIOD = 20 ns HIGH 50% INPUT_JITTER 0 ns; divided by 4.50 to 4.444 nS and duty cycle corrected to HIGH 2.222 nS
        For more information, see Period Analysis in the Timing Closure User Guide (UG612).
        48703 paths analyzed, 8504 endpoints analyzed, 0 failing endpoints
        0 timing errors detected. (0 setup errors, 0 hold errors, 0 component switching limit errors)
        Minimum period is 4.432ns.
        -------------------------------------------------------------------------------- [/code]

        No i poszło

        32 bity to i tak maksymalne słowo audio jakie idzie przesłać, więc nie ma sensu zwiększać je do 35 bitów, ale 3 dodatkowe bity na współczynnik filtru to naprawdę dużo, ponieważ zmniejsza błędy kwantyzacji z typu double na fixed-point.

        Skomentuj


          #5
          Informacja
          Art. na glowna strone bylby fajny

          Skomentuj


            #6
            Myślę, że artykuł powinien być bardziej rozwinięty Na pewno w przyszłości coś dopiszę, ale tego jest tyle, że nie wiadomo za co się zabrać i jak to rozwinąć

            Skomentuj


              #7
              Teraz może wyjaśnienie dlaczego te dodatkowe 3 bity dla współczynników (z 32 bitów na 35 bitów) jest dosyć znaczące w tym projekcie

              Zapis współczynnika filtru typu double-precision floating-point:

              [code]-0.0000000020745233141206077[/code]

              Zapis z floating-point na fixed-point (odpowiednio 32 oraz 35 bitowy):

              [code]-0.0000000020745233141206077 * 2^31 = -4.45~ = -4
              -0.0000000020745233141206077 * 2^34 - -35.64~ = -36[/code]

              Skalujemy całą liczbę, która składa się tylko z części ułamkowej przez mnożenie całości do n'tej potęgi dwójki (2^31 dla 32 bitów oraz 2^34 dla 35 bitów, jeden bit jest dla znaku współczynnika, tj. liczba ujemna lub dodatnia). Zaokrąglamy wynik do najbliżej wartości, ponieważ po skalowaniu nie da się zapisać części liczby po przecinku w formacie fixed-point.

              Zapis z fixed-point (odpowiednio 32 oraz 35 bitowy) z powrotem na double-precision floating-point:

              [code] -4 / 2^31 = -0.00000000186264515
              -36 / 2^34 = -0.00000000209547579[/code]

              Błędy (różnice między faktycznym współczynnikiem a po konwersji na fixed-point z N bitami odpowiednio dla 32 oraz 35 bitów):

              [code]0.00000000021187816
              0.000000000020952476[/code]

              Różnica w błędzie zapisu około 10.11~ razy lepsza dla dodatkowych 3 bitów. W teorii powinna dążyć do 8 (2^3) zależnie od współczynnika i jego zaokrąglenia, które może przechylić w jedną lub drugą stronę. Różnica warta zachodu i dlatego się z tym męczyłem
              Last edited by .3lite; 22.08.2018, 18:42.

              Skomentuj


                #8
                No i dzisiaj jest jedna z tych chwil w których ten projekt zaczyna się robić jeszcze bardziej ciekawy

                Filtr dostał dzisiaj częściową re-konfigurację PLLki Co to oznacza? Filtr nie używa już zegara MCLK do taktowania danych do DAC'a a sam dobiera częstotliwość z którą taktuje dane do przetwornika. W momencie zmiany długości słowa (16, 18, 20 lub 24 bitów) wewnętrzna PLLka się re-konfiguruje na nową częstotliwość i tak o to mamy poniższe częstotliwości zależne od słowa wyjściowego:

                16 bitów - 13.5 MHz.
                18 bitów - 15.0 MHz.
                20 bitów - 16.5 MHz.
                24 bity - 19.5 MHz.

                Inaczej pisząc ten filtr potrafi taktować takie przetworniki jak PCM56 czy AD1865 z częstotliwością 768 kHz :)

                Jak to wygląda w praktyce? Zatrzask danych jest asynchroniczny względem PLLki, więc tylko i wyłącznie od niego zależy jitter:

                16 bitów:



                18 bitów:



                20 bitów:



                24 bity:



                Ale może jednak jitter!?:



                Jak widać nie

                Dodatkowe pomiary:





                Filtr posiada wejście I2S:

                MCLK
                BCLK
                LRCK
                DATA

                Z czego MCLK może być podłączone razem z BCLK (po prostu zwarte). MCLK jest tylko i wyłącznie używane do wygenerowania zatrzasku (LE) przetwornika oraz wyciągania danych z FIFO, więc tylko i wyłącznie od niego zależy jitter całości Filtr potrafi rozpoznać i zaakceptować MCLK zaczynające się od 1.4412 MHz / 1.4536 MHz a kończąc na 45.1584 MHz / 49.152 MHz. W przypadku gdy BCLK jest 64x Fs może on służyć też za MCLK Nie ma to żadnej różnicy, co lepszej jakości, byle by był synchroniczny względem wyciągania danych nadajnika (MCLK nie może być asynchroniczny bo się po prostu rozjedzie kolejka FIFO).
                Last edited by .3lite; 24.08.2018, 23:48.

                Skomentuj


                  #9
                  Zamieszczone przez .3lite Zobacz posta
                  Filtr dostał dzisiaj częściową re-konfigurację PLLki Co to oznacza? Filtr nie używa już zegara MCLK do taktowania danych do DAC'a a sam dobiera częstotliwość z którą taktuje dane do przetwornika. W momencie zmiany długości słowa (16, 18, 20 lub 24 bitów) wewnętrzna PLLka się re-konfiguruje na nową częstotliwość i tak o to mamy poniższe częstotliwości zależne od słowa wyjściowego:
                  Genialna sprawa
                  marcinsywala.pl
                  Cambridge Audio Stream Magic 6 | Power Amplifier Class D Hypex 2x Ncore NC2k | T29MF001 + MW16P-4 + 2xTIW 200 XS

                  Skomentuj


                    #10
                    Szczerze pisząc to mało powiedziane Nie sądziłem, że uda się to zmieścić pod względem spełnienia wymagań czasowych. Dane przechodzą przez trzy domeny czasowe (I2S => 225 MHz rdzenia => PLL) i jest to cholernie trudno ogarnąć pod względem naruszeń typu setup / hold w FPGA. Tym bardziej, że musisz mieć pewne flagi pomiędzy różnymi domenami czasowymi. Może kogoś zaciekawi ten temat:

                    https://en.wikipedia.org/wiki/Metast..._(electronics)
                    https://www.fpga4fun.com/CrossClockDomain.html
                    https://www.nandland.com/articles/cr...n-an-fpga.html

                    W każdym wypadku daleko do końca już nie ma. Na sam koniec zostawiam liczenie współczynników i ich odsłuch, ponieważ to mały pikuś :)

                    Skomentuj


                      #11
                      Przecierasz nowe ścieżki, czy już jakaś firma takie rozwiązania stosowała w swoich przetwornikach.

                      Skomentuj


                        #12
                        Na pewno znajdzie się wiele filtrów na FPGA w świecie audio, ale nigdy się tym nie interesowałem, więc po prostu nie wiem

                        Po ostatnich zmianach tak wygląda analiza czasowa dla 225 MHz (najwolniejsza część projektu):

                        [code] ================================================== ==============================
                        Timing constraint: PERIOD analysis for net "CLK_225MHz_int" derived from NET "CLK_50MHz_IBUFG" PERIOD = 20 ns HIGH 50% INPUT_JITTER 0 ns; divided by 4.50 to 4.444 nS and duty cycle corrected to HIGH 2.222 nS
                        For more information, see Period Analysis in the Timing Closure User Guide (UG612).
                        48341 paths analyzed, 8517 endpoints analyzed, 0 failing endpoints
                        0 timing errors detected. (0 setup errors, 0 hold errors, 0 component switching limit errors)
                        Minimum period is 4.292ns.
                        --------------------------------------------------------------------------------

                        Paths for end point coeffs_rom/DATA_OUTB_27 (SLICE_X12Y34.B5), 1 path
                        --------------------------------------------------------------------------------
                        Slack (setup path): 0.152ns (requirement - (data path - clock path skew + uncertainty))
                        Source: coeffs_rom_Mram_coefficients_int14 (RAM)
                        Destination: coeffs_rom/DATA_OUTB_27 (FF)
                        Requirement: 4.444ns
                        Data Path Delay: 4.024ns (Levels of Logic = 1)
                        Clock Path Skew: -0.088ns (0.599 - 0.687)
                        Source Clock: CLK_225MHz_int_BUFG rising at 0.000ns
                        Destination Clock: CLK_225MHz_int_BUFG rising at 4.444ns
                        Clock Uncertainty: 0.180ns

                        Clock Uncertainty: 0.180ns ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE
                        Total System Jitter (TSJ): 0.070ns
                        Total Input Jitter (TIJ): 0.000ns
                        Discrete Jitter (DJ): 0.288ns
                        Phase Error (PE): 0.000ns

                        Maximum Data Path at Slow Process Corner: coeffs_rom_Mram_coefficients_int14 to coeffs_rom/DATA_OUTB_27
                        Location Delay type Delay(ns) Physical Resource
                        Logical Resource(s)
                        ------------------------------------------------- -------------------
                        RAMB16_X1Y24.DOA1 Trcko_DOA 2.100 coeffs_rom_Mram_coefficients_int14
                        coeffs_rom_Mram_coefficients_int14
                        SLICE_X12Y34.B5 net (fanout=1) 1.724 coeffs_rom/read_data_outb_int<27>
                        SLICE_X12Y34.CLK Tas 0.200 coeffs_rom/DATA_OUTB<28>
                        coeffs_rom/read_data_outb_int<27>_rt
                        coeffs_rom/DATA_OUTB_27
                        ------------------------------------------------- ---------------------------
                        Total 4.024ns (2.300ns logic, 1.724ns route)
                        (57.2% logic, 42.8% route)

                        -------------------------------------------------------------------------------- [/code]

                        Slack na poziomie 0.152 ns - taki zapas do granicy, nic tylko liczyć więcej W tym wypadku "Minimum period is 4.292ns." wskazuje, że projekt może pracować przy 232.99 MHz. Oczywiście koniec końców zależy to od ułożenia elementów w FPGA przez takie procesy jak MAP i PAR, więc przy dobrych wiatrach można zyskać trochę więcej, ale nie ma to żadnego sensu.

                        Na razie nic nowego raczej nie planuję dodawać, więc pewnie czas zacząć projektować PCB
                        Last edited by .3lite; 24.08.2018, 10:44.

                        Skomentuj


                          #13
                          Poprawiłem troszkę PLLkę i dodałem po 0,5 MHz do każdego słowa wyjściowego (16 bitów - 14 MHz, 18 bitów - 15.5 MHz, 20 bitów - 17 MHz oraz 24 bity - 20 MHz). Po prostu wolę mieć dwa dodatkowe takty (<ilość bitów + 2> * 768 < częstotliwość PLLki) dla procesu od PLLki na zareagowanie względem asynchronicznego zbocza zatrzasku. Dodatkowo doszło następne FIFO (w sumie jest już 6 kolejek dla obu kanałów ), które zaczyna taktować dane do przetwornika dopiero jak będzie co najmniej w połowie pełne. Poniżej pomiary oraz pasmo przenoszenia filtru:

                          20 kHz:



                          Biały szum 48 kHz:



                          Biały szum 44.1 kHz:



                          Jitter:



                          Dodatkowo:

                          [code] ================================================== ==============================
                          Timing constraint: PERIOD analysis for net "CLK_225MHz_int" derived from NET "CLK_50MHz_IBUFG" PERIOD = 20 ns HIGH 50% INPUT_JITTER 0 ns; divided by 4.50 to 4.444 nS and duty cycle corrected to HIGH 2.222 nS
                          For more information, see Period Analysis in the Timing Closure User Guide (UG612).
                          48391 paths analyzed, 8522 endpoints analyzed, 0 failing endpoints
                          0 timing errors detected. (0 setup errors, 0 hold errors, 0 component switching limit errors)
                          Minimum period is 4.168ns.
                          --------------------------------------------------------------------------------

                          Paths for end point mac2/pipe_int_6_66 (SLICE_X6Y25.CIN), 192 paths
                          --------------------------------------------------------------------------------
                          Slack (setup path): 0.276ns (requirement - (data path - clock path skew + uncertainty))
                          Source: mac2/Mmult_input1_int[31]_input2_int[34]_MuLt_0_OUT3 (DSP)
                          Destination: mac2/pipe_int_6_66 (FF)
                          Requirement: 4.444ns
                          Data Path Delay: 3.893ns (Levels of Logic = 9)
                          Clock Path Skew: -0.095ns (0.627 - 0.722)
                          Source Clock: CLK_225MHz_int_BUFG rising at 0.000ns
                          Destination Clock: CLK_225MHz_int_BUFG rising at 4.444ns
                          Clock Uncertainty: 0.180ns

                          Clock Uncertainty: 0.180ns ((TSJ^2 + TIJ^2)^1/2 + DJ) / 2 + PE
                          Total System Jitter (TSJ): 0.070ns
                          Total Input Jitter (TIJ): 0.000ns
                          Discrete Jitter (DJ): 0.288ns
                          Phase Error (PE): 0.000ns [/code]

                          W praktyce 240 MHz
                          Last edited by .3lite; 25.08.2018, 09:28.

                          Skomentuj


                            #14
                            Mała aktualizacja - wejście I2S filtru nie jest już ograniczone na BCLK do 64x Fs. W praktyce nie ma już żadnego ograniczenia (może być nawet 4x Fs lub 423423x Fs), ponieważ tak czy siak tylko najstarsze 32 bity zostaną zapisane. Najważniejszy jest w tym wypadku LRCK (Fs) i to on decyduje o zatrzaśnięciu danych i przerzuceniu do wejściowego FIFO. Oczywiście mając jakiś egzotyczny BCLK nie będzie możliwe użycie go jako MCLK, ale typowe wartości 32x Fs, 64x Fs lub 128x Fs będą akceptowalne jako MCLK.

                            Filtr obsługuje poniższe częstotliwości na MCLK:

                            49.152 MHz
                            45.1584 MHz
                            36.864 MHz *
                            33.8688 MHz *
                            24.576 MHz
                            22.5792 MHz
                            18.432 MHz *
                            16.9344 MHz *
                            12.288 MHz
                            11.2896 MHz
                            9.2160 MHz *
                            8.4672 MHz *
                            6.144 MHz
                            5.6448 MHz
                            4.608 MHz *
                            4.2336 MHz *
                            3.072 MHz
                            2.8224 MHz
                            1.536 MHz
                            1.4112 MHz

                            Nie bez powodu jest lista, ponieważ filtr musi wiedzieć jak podzielić częstotliwość aby otrzymać 705.6 kHz lub 768 kHz. Tak samo nie bez powodu dodałem te z gwiazdką (*), ponieważ są to raczej częstotliwości występujące w odtwarzaczach CD W teorii dodając rezystor (170~ Ohm) na wejścia I2S można go podłączyć pod logikę 5V i powinno chodzić. Na pewno sam to sprawdzę jak będę miał już gotową wersję samego filtru.

                            Na razie to tyle. W dniu wczorajszym zamówiłem docelowe PCB i jak się nigdzie nie walnąłem to powinno działać

                            Skomentuj


                              #15
                              PCM56 bez żadnego problemu przyjął na siebie strumień 768 kHz i śmiga:









                              Oczywiście DAC na pająka to żaden DAC, ale miało działać do testów

                              Skomentuj


                                #16
                                To ja robię pcb pod różnicowe PCMXX :)

                                Skomentuj


                                  #17
                                  Zapas ten przetwornik na pewno ma dosyć duży, ponieważ miałem błąd w którym oscylator nie zmienił częstotliwości i latał na 20 MHz a PCM56 dalej śmigał Tym bardziej będzie latał przy 14 MHz bez nadwyrężania się jakoś nadzwyczajnie.

                                  Skomentuj


                                    #18
                                    I po co komu 4 warstwowe PCB, jak starczy płytka uniwersalna?

                                    Skomentuj


                                      #19
                                      Jakie są realne korzyści dla dźwięku ?

                                      Skomentuj


                                        #20
                                        Korzyści dla dźwięku na pewno są dosyć znaczące Filtr posiada 8192 współczynników oraz wykonuje obliczenia 32x35. Nowa próbka tworzona jest na bazie 512 poprzednich próbek (interpolacja 16x). Dodatkowo można śmigać na takich przetwornikach jak PCM56 czy AD1865 do 768 kHz.

                                        Z tego co wiem na rynku nie istnieje inny filtr interpolacyjny, który ma takie możliwości (jest jeszcze filtr kolegi olog z AS, który miał inne założenia projektowe, więc wszystko zależy od potrzeb). Sonicznie już do do własnej oceny Plusem tego wszystkiego jest to, że filtr z technicznego punktu widzenia jest takim kontenerem na współczynniki, które można zmienić i to one odpowiadają za to jak ten filtr działa oraz jak gra.
                                        Last edited by .3lite; 01.09.2018, 10:06.

                                        Skomentuj

                                        Czaruję...
                                        X