Skocz do zawartości

Sipeed Tang Nano 4k z ADC - Gowin FPGA Designer


Gieneq

Pomocna odpowiedź

Cześć,

w międzyczasie, dopóki nie dotrze zakupiona płytka z przetwornikiem ADC 24-bit/30ksps/SPI (IC AD1256) rozejrzałem się za dokumentacją i bibliotekami w języku C dla tego układu scalonego. Tutaj link do modułu przetwornika analogowo-cyfrowego zakupionego na Aliexpress.com:

https://pl.aliexpress.com/item/1005001593721645.html?gatewayAdapt=glo2pol&spm=a2g0o.9042311.0.0.72bd5c0f8AAiQH

Tutaj datasheet do układu ADS1256:

https://www.ti.com/lit/ds/symlink/ads1256.pdf

Tutaj linki do bibliotek z języku C dla układu AD1256 (STM32 HAL i Arduino IDE):

 

https://github.com/eta-systems/ADS1255

https://github.com/ADS1xxx-Series-ADC-Libraries/ADS1255-ADS1256

---------------------------------------------------------------------------------------------------------------------

Tutaj link do drugiej płytki z tanim ADC audio (z interface I2S):

https://pl.aliexpress.com/item/32830812025.html?gatewayAdapt=glo2pol&spm=a2g0o.9042311.0.0.72bd5c0f8AAiQH

A tutaj datasheet do IC ADC PCM1808:

https://www.ti.com/lit/ds/symlink/pcm1808.pdf?ts=1645346815027&ref_url=https%3A%2F%2Fwww.ti.com%2Fdocument-viewer%2FPCM1808%2Fdatasheet%2Fspecifications

Tutaj link do biblioteki C++ dla układu PCM1808:

https://github.com/alislahish/Alislahish-PCM1808

Myślę ,że te informacje będą przydatne gdyby ktoś próbował użyć jednego z tych modułów. Także napisanie kodu w Verilogu do interakcji z powyższymi układami z  FPGA jest łatwiejsze, jeśli możemy przeanalizować jak biblioteka w języku C jest napisana.

Pozdrawiam

 

  • Lubię! 1
Link do komentarza
Share on other sites

GENERACJA TESTOWEGO SYGNAŁU AUDIO DLA IPCORE FFT (Z GOWINEDA):

Po krótkiej analizie kodu biblioteki w języku C dla układu ADC ADS1256 stwierdziłem, że do odbioru sampli potrzebne będzie napisanie biblioteki w języku Verilog (prosty IPCore dla SPI będzie niewystarczający, choć biblioteka będzie z niego korzystała). Zatem część kodu, który mogę napisać, dopóki nie przyjdzie do mnie płytka modułu przetwornika ADC dotyczy IP Core z modułem FFT. Sprawdziłem w dokumentacji IP Cores firmy Gowin, że dostępny jest IP Core symulujący pamięć ROM i pragnę z niego skorzystać. W pamięci ROM będą zapisywane sample 18-bitowe sygnału audio (pasmo do 15 KHz, wynikające z prędkości ADC 30ksps). Pamięć będzie zawierała 4096 próbek 18-to bitowych (przebieg audio w dziedzinie czasu. Wygeneruję sygnał audio, który składa się z pięciu sygnałów sinusoidalnych o różnych częstotliwościach, amplitudzie i fazie początkowej. Do generacji sampli (audio) użyję programu napisanego w C# w VisualStudio2019 (Comunity Edition) Microsoftu ponieważ dobrze znam ten język i IDE. Program wygeneruje plik testowy z którego będzie można wczytać do ROM'u sample przebiegu audio. Poza tym program narysuje wykresy sygnału audio w dziedzinie czasu i częstotliwości (dwa różne). Aby to zrobić może odświeżę pamięć czytających odnośnie tego jak matematycznie modelujemy sygnały audio- sinusoidalne (tak, aby napisać model sygnału np. w programie w języku C). Musimy przypomnieć sobie kilka prostych pojęć: co to jest okres przebiegu, jego częstotliwość, pulsacja i jakim równaniem opisujemy dowolny sygnał okresowy. A więc po kolei:

1) Co to jest częstotliwość przebiegu i okres:

https://pl.wikipedia.org/wiki/Częstotliwość

2) Czym jest pulsacja i jaka jest zależność pulsacji i częstotliwości:

https://pl.wikipedia.org/wiki/Pulsacja

3) Jakie jest równanie pojedynczego sygnału sinusoidalnego = Patrz ten link "Sygnał w dziedzinie czasu":

https://wazniak.mimuw.edu.pl/index.php?title=Układy_elektroniczne_i_technika_pomiarowa/Moduł_10

UETP_M10_Slajd03.thumb.png.61989b80cd24aaf4623efc91e7eb898b.pngUETP_M10_Slajd04.thumb.png.10fa0c54df2cf805ddadeb79d304818a.pngUETP_M10_Slajd05.thumb.png.93a6b8ab808fc29b924b02412bb7e320.png

Po podstawieniu za pulsację ω=2πf - gdzie f to częstotliwość sinusoidy do równania z punktu 3 otrzymujemy:

x(t)=X0+A*cos(ω*t+φ0)

=> x(t)=X0+A*cos(2*π*f*t+φ0)

gdzie:

x(t) - jest to opis przebiegu sinusoidalnego w dziedzinie czasu,

X0 - jest wartością składowej stałej przebiegu (w Woltach),

A - jest to amplituda przebiegu (w Woltach),

cos - jest to funkcja trygonometryczna cosinus,

π - jest to stała = 3,141592 (w przybliżeniu),

f - jest częstotliwością przebiegu w Hertzach

t - jest czasem,

φ0 - jest fazą początkową przebiegu (kątem podanym w radianach)

Ważna jest jeszcze jedna rzecz (wynikająca z Analizy Fouriera) - każdy przebieg okresowy można przedstawić jako sumę dowolnej liczby przebiegów sinusoidalnych (o konkretnej amplitudzie, częstotliwości i fazie początkowej). Uzbrojeni w taką wiedzę możemy napisać program w języku C# (składnia praktycznie identyczna z językiem C) do generacji próbek audio. Jak widać będzie nam potrzebna funkcja cosinus, oraz wartość stałej π z biblioteki matematycznej języka C#. Program po wyliczeniu wartości cosinus i obliczeniu wartości sygnału w czasie będzie musiał zamienić wartość - liczbę zmienno- pozycyjną (Double lub Float) na osiemnasto-bitową liczbę całkowitą oraz zapisać do pliku tekstowego tą wartość jako liczbę heksadecymalną. Z tego pliku będą wczytywane wartości do pamięci ROM (IP Core) w układzie FPGA Gowin. Jak będę miał napisany program w C# do generacji przebiegu audio - napiszę o tym w tym wątku.

Pozdrawiam

 

  • Lubię! 1
Link do komentarza
Share on other sites

Cześć @FlyingDutch  super, dziękuję za zaangażowanie 🙂 

Sm niedawno generowałem próbki, tylko że w pythonie.

19 godzin temu, FlyingDutch napisał:

każdy przebieg okresowy można przedstawić jako sumę dowolnej liczby przebiegów sinusoidalnych

To jest jeszcze dość prosta sprawa, ale w IP Core nie widzę jednej rzeczy - funkcji okna, bez niej próbki będą miały załamanie przy przejściu pomiędzy początkiem i końcem co wniesie skok czyli bardzo wysoką częstotliwość w sygnale. Napisanie własnego okna nie jest trudne, ale jak myślisz, czy jest to gdzieś zaszyte w tym IPCore?

Przy okazji nie wątpię że dobrze znasz temat FFT, ale bardzo mi się podoba ten film i polecam obejrzeć 🙂 

 

A tu jest ciekawie wytłumaczony algorytm optymalizacji - przestawianie indeksów, dlaczego liczba próbek powinna być potęgą 2. To są technikalia, których pewnie się w tym projekcie nie dotknie ale zawsze warto wiedzieć 🙂 

 

 

  • Lubię! 1
Link do komentarza
Share on other sites

48 minut temu, Gieneq napisał:

Cześć @FlyingDutch  super, dziękuję za zaangażowanie 🙂 

Sm niedawno generowałem próbki, tylko że w pythonie.

To jest jeszcze dość prosta sprawa, ale w IP Core nie widzę jednej rzeczy - funkcji okna, bez niej próbki będą miały załamanie przy przejściu pomiędzy początkiem i końcem co wniesie skok czyli bardzo wysoką częstotliwość w sygnale. Napisanie własnego okna nie jest trudne, ale jak myślisz, czy jest to gdzieś zaszyte w tym IPCore?

Przy okazji nie wątpię że dobrze znasz temat FFT, ale bardzo mi się podoba ten film i polecam obejrzeć 🙂 

A tu jest ciekawie wytłumaczony algorytm optymalizacji - przestawianie indeksów, dlaczego liczba próbek powinna być potęgą 2. To są technikalia, których pewnie się w tym projekcie nie dotknie ale zawsze warto wiedzieć 🙂 

 

 

Cześć @Gieneq,

pierwszego tematu nie analizowałem, natomiast drugi temat jest mi znany i podejrzewan, że takie optymalizacje są zawarte w kodzie IP Core "FFT" (jest to raczej standard). Czy mógłbyś udostepnić ten skrypt w Pythonie do generacji sampli przebiegu audio?

Pozdrawiam

 

Edytowano przez FlyingDutch
updaTe
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

22 godziny temu, Gieneq napisał:

Cześć @FlyingDutch  super, dziękuję za zaangażowanie 🙂 

Sm niedawno generowałem próbki, tylko że w pythonie.

To jest jeszcze dość prosta sprawa, ale w IP Core nie widzę jednej rzeczy - funkcji okna, bez niej próbki będą miały załamanie przy przejściu pomiędzy początkiem i końcem co wniesie skok czyli bardzo wysoką częstotliwość w sygnale. Napisanie własnego okna nie jest trudne, ale jak myślisz, czy jest to gdzieś zaszyte w tym IPCore?

Cześć @Gieneq,

a jakiej funkcji okna chciałbyś użyć i okno jakiej szerokości? Jak chciałbyś to zaimplementować od strony matematycznej w kodzie Verilog? Masz jakiś pomysł, jeśli tak to napisz jaki postaram się to zaimplementować w Verilog'u.

BTW: zajrzałem do dokumentacji IPCore FFT (Gowin) i raczej nie jest w nim zaimplementowana funkcja okna. Przejrzałem też dokumentację IPCore "FFT" firmy Xilinx, ale tam też nie ma żadnej wzmianki na temat okna.

Tutaj jest link jak zaimplementować funkcję okna w VHDL'u, ale nie jest to trywialne:

https://discourse.world/h/2018/10/23/Features-of-window-filtering-on-FPGA

Zadałem pytanie na temat implementacji "window filtering" w IP Core FFT i dostałem odpowiedź, że ta funkcja jest oddzielnym krokiem przetwarzania przy obróbce sygnałów i dlatego nie jest zaimplementowana w FFT. Polecono mi użycia filtru dolnoprzepustowego, w szczególności filtru "wędrująca średnia" (moving Average), który jest stosunkowo prosty do implementacji. Tutaj link do przykładowej implementacji w Verilog'u:

https://www.allaboutcircuits.com/technical-articles/implementing-a-low-pass-filter-on-fpga-with-verilog/

Pozdrawiam

Edytowano przez FlyingDutch
update
Link do komentarza
Share on other sites

Cześć @Gieneq,

Mam już wygenerowane 2**14 = 16384 sample przebiegu audio, przy użyciu biblioteki Numpy w Pythonie (zainstalowałem sobie na kompie Anacondę z pythonem 3.9). Mój sygnał audio zawiera 16384 próbki z sygnału będącego sumą siedmiu sinusoid o różnych częstotliwościach i amplitudach;

1) Amp=2 f=50Hz

2) Amp=3 f=200Hz

3) Amp=1,8 f=1 KHz

4) Amp=0,9 f=6,5 KHz

5) Amp=2,4 f=10 KHz

6) Amp=3,7 f=12,4 KHz

6) Amp=2 f=14,8 KHz

Założyłem w kodzie programu w Pythonie, że częstotliwość próbkowania wynosi 30 Khz (bo taką prędkość może osiągnąć wybrany przetwornik ADC).

Tutaj kod w Pythonie generujący próbki i obliczający FFT, oraz rysujący wykres obliczonego widma:

%matplotlib inline
import numpy as np
import scipy.signal as sig
from scipy.io import wavfile
import matplotlib.pyplot as plt

# częstotliwość próbkowania
fs = 30000

n = np.arange(16384) #2**14
sinus = 2*np.sin(2 * np.pi * n * 50 / fs)+3*np.sin(2 * np.pi * n * 200 / fs)+1.8*np.sin(2 * np.pi * n * 1000 / fs)+0.9*np.sin(2 * np.pi * n * 6500 / fs)+2.4*np.sin(2 * np.pi * n * 10000 / fs)+3.7*np.sin(2 * np.pi * n * 12400 / fs)+2*np.sin(2 * np.pi * n * 14800 / fs)
widmo = np.fft.fft(sinus)

widmo_amp = np.abs(np.fft.rfft(sinus)) / 1024
f = np.fft.rfftfreq(16384, 1/fs)
plt.plot(f, widmo_amp)
plt.xlabel('częstotliwość [Hz]')
plt.ylabel('amplituda widma')
plt.title('Widmo "rzeczywiste" sygnału sinusoidalnego')
plt.show()

A tutaj zrzut ekranu z "Jupyter Notebook"z tymi obliczeniami:

JupyterNot1.thumb.png.10058ba5b566d21b1d10224f7bd96328.png

Jak widać na wykresie obliczona transformata FFT jest prawidłowa - patrz wykres widma. Teraz muszę tylko zmodyfikować program tak aby w pliku zapisał wygenerowane sample przebiegu audio (te 16384 próbki) w formacie w którym będę mógł wczytać dane z tego pliku do IP Core "ROM" - chyba najpierw wypróbuję kod dla FPGA Xilinx'a (które lepiej znam), a jak wyniki będą prawidłowe wtedy to samo postaram się uruchomić na FPG'a Gowin (Tang Nano 4K).

Tutaj link do artykułu z kodem FFT (z użyciem pakietu Numpy) w Pythonie:

https://sound.eti.pg.gda.pl/~greg/dsp/01-AnalizaWidmowa.html

Hej @Gieneq daj znać jak Tobie idą prace.

BTW: Jeślibyśmy liczyli FFT z tych 2**14 (16384) sampli przychodzących z przetwornika ADC (ADS1256) to wykres widma mógłby być odświeżany 2 razy na sekundę, czy to by wystarczyło? Sama FFT będzie się liczyła bardzo szybko (w zależności od zastosowanego układu FPGA), ale czas zebrania 16384 sampli z prędkością 30Ksps będzie trwał około pół sekundy.

Update: udało mi się zapisać wygenerowane sample (2**14), przebieg w dziedzinie czasuw pliku tekstowym - w każdym wierszu jest jedna wartość sampla jako liczba float64 -Double). Tutaj wklejam plik z samplami spakowany zipem:

PrzbiegfunkcjaT.zip

Trzeba było zmienić kształt tablicy n i sinus na jeden wiersz i 2**14 kolumn, aby móc zapisać próbki w pliku - patrz kod:

sinus2=sinus.reshape((1,16384)) #trzeba zmienic shape, aby zapis w pliku byl mozliwy
n2=n.reshape((1,16384))
print('Array:', sinus2.shape)
print('Datatype:', sinus2.dtype)

Daje to w wyniku:

Array: (1, 16384)

Datatype: float64

Teraz takim kodem, można było zapisać tablice sinus2 w pliku:

a_file = open("test.txt", "w")
for row in sinus2:
    np.savetxt(a_file,row)
a_file.close()

A tym kodem zrobiłem wykres przebiegu sygnału (próbek) jako funkcję czasu:

plt.plot(n, sinus)
plt.xlabel('czas [s]')
plt.ylabel('amplituda')
plt.title('Przebieg sygnału sinusoidalnego')
plt.show()

Jak widać na zrzucie ekranu poniżej, w trakcie zbierania 16384 próbek wartości przebiegu wielokrotnie się powtarzają:

JupyterNot2.thumb.png.dc3eabceb3a7465c20cc30650d156a1c.png

Teraz muszę wykombinować jak z zapisanego pliku z samplami jako float64 zrobić plik, który będzie można wczytać do pamięci ROM w układzie FPGA (IP Core ROM). Jak te wartości będą w pamięci ROM (układ FPGA) to będzie można na tym policzyć FFT (też za pomocą IP Core.

Pozdrawiam

Edytowano przez FlyingDutch
Link do komentarza
Share on other sites

Cześć @FlyingDutch dziękuję za zaangazowanie, dopiero jutro będę miał dostęp do komputra, ale cały czas uczę się Veriloga. Teraz przerabiam przykłady znalezione w internecie i trochę jestem zaskoczony, że do wszystkiego trzeba podchodzić ze sporym dystansem. W przypadku zwykłych językow programowania są jakieś prawdy oczywiste, a tu widzę, że jak zapoznałem się z taką tabelką wytycznych używania always @ 

image.thumb.png.17747b5bd0fd9a499d1ad34076565b78.png

to w dalszym przykładzie widzę brak konsekwencji...

always @(clk, currentState, count)
begin
    if(count<=x)
        currentState = continueState;
    else
        currentState = stopState;
end

sygnał x nie jest na liście a podobno powinien być, clk zazwyczaj miał warunek if(clk) tu nie ma nic. Podobno begin-end nie jest potrzebne dla takiego warunku. Ehh... Wyklaruje się kiedyś :) 

Dlatego staram się porównywać przykłady, żeby jakoś wyważyć to czego się uczę.

 

1 godzinę temu, FlyingDutch napisał:

przy użyciu biblioteki Numpy w Pythonie

Dobry pomysł.

Dnia 21.02.2022 o 09:03, FlyingDutch napisał:

Czy mógłbyś udostepnić ten skrypt w Pythonie do generacji sampli przebiegu audio?

Tu o tym wspomminałem:

https://forbot.pl/forum/topic/20787-wyswietlacz-widma-audio---esp32-i-399-ws2812b/page/2/?tab=comments#comment-172882

Akurat robiłem to wtedy bez Numpy, bo potrzebowałem tylko obejrzeć jak wyglądają próbki i zapisać do pliku.

import math
import random
from matplotlib import pyplot as plt
octaves = 6
base_freq = 1 #Hz
samples_count = 128
min_val = 40
max_val = 255

samples = [0.0]*samples_count
samples_int = [0]*samples_count


for ocateve in range(1, octaves+1):
    print(f"Octave {ocateve}")
    freq = base_freq * ocateve
    amplitude = 1
    phase = random.random()
    for i in range(samples_count):
        samples[i] += amplitude * math.sin(phase + 2*math.pi*freq*i/samples_count)

Z oknami kojarzę, że jest to inny etap przetwarzania. W kodzie Arduino FFT który używałem do tej pory masz metodę Windowing:

void arduinoFFT::Windowing(uint8_t windowType, uint8_t dir)
{// Weighing factors are computed once before multiple use of FFT
// The weighing function is symmetric; half the weighs are recorded
	double samplesMinusOne = (double(this->_samples) - 1.0);
	for (uint16_t i = 0; i < (this->_samples >> 1); i++) {
		double indexMinusOne = double(i);
		double ratio = (indexMinusOne / samplesMinusOne);
		double weighingFactor = 1.0;
		// Compute and record weighting factor
		switch (windowType) {
		case FFT_WIN_TYP_RECTANGLE: // rectangle (box car)
			weighingFactor = 1.0;
			break;
		case FFT_WIN_TYP_HAMMING: // hamming
			weighingFactor = 0.54 - (0.46 * cos(twoPi * ratio));
			break;
		case FFT_WIN_TYP_HANN: // hann
			weighingFactor = 0.54 * (1.0 - cos(twoPi * ratio));
			break;
		case FFT_WIN_TYP_TRIANGLE: // triangle (Bartlett)
			#if defined(ESP8266) || defined(ESP32)
			weighingFactor = 1.0 - ((2.0 * fabs(indexMinusOne - (samplesMinusOne / 2.0))) / samplesMinusOne);
			#else
			weighingFactor = 1.0 - ((2.0 * abs(indexMinusOne - (samplesMinusOne / 2.0))) / samplesMinusOne);
			#endif
			break;
		case FFT_WIN_TYP_NUTTALL: // nuttall
			weighingFactor = 0.355768 - (0.487396 * (cos(twoPi * ratio))) + (0.144232 * (cos(fourPi * ratio))) - (0.012604 * (cos(sixPi * ratio)));
			break;
		case FFT_WIN_TYP_BLACKMAN: // blackman
			weighingFactor = 0.42323 - (0.49755 * (cos(twoPi * ratio))) + (0.07922 * (cos(fourPi * ratio)));
			break;
		case FFT_WIN_TYP_BLACKMAN_NUTTALL: // blackman nuttall
			weighingFactor = 0.3635819 - (0.4891775 * (cos(twoPi * ratio))) + (0.1365995 * (cos(fourPi * ratio))) - (0.0106411 * (cos(sixPi * ratio)));
			break;
		case FFT_WIN_TYP_BLACKMAN_HARRIS: // blackman harris
			weighingFactor = 0.35875 - (0.48829 * (cos(twoPi * ratio))) + (0.14128 * (cos(fourPi * ratio))) - (0.01168 * (cos(sixPi * ratio)));
			break;
		case FFT_WIN_TYP_FLT_TOP: // flat top
			weighingFactor = 0.2810639 - (0.5208972 * cos(twoPi * ratio)) + (0.1980399 * cos(fourPi * ratio));
			break;
		case FFT_WIN_TYP_WELCH: // welch
			weighingFactor = 1.0 - sq((indexMinusOne - samplesMinusOne / 2.0) / (samplesMinusOne / 2.0));
			break;
		}
		if (dir == FFT_FORWARD) {
			this->_vReal[i] *= weighingFactor;
			this->_vReal[this->_samples - (i + 1)] *= weighingFactor;
		}
		else {
			this->_vReal[i] /= weighingFactor;
			this->_vReal[this->_samples - (i + 1)] /= weighingFactor;
		}
	}
}

Jak widać okno prostokątne to banał, ale właśnie problem jest z krańcami. Dlatego najprostsza funkcja sinc() już byłaby (tak sądzę) lepsza.

Dnia 21.02.2022 o 11:28, FlyingDutch napisał:

że ta funkcja jest oddzielnym krokiem przetwarzania przy obróbce sygnałów i dlatego nie jest zaimplementowana w FFT.

Tak, w kodzie który używałem dla ESP32 pierwszym etapem było "DCRemoval" gdzie odejmuje się wartość średnią, czyli to co reprezentuje napięcie stałe, następne wchodzi okno, którym działa się na próbkach i dopiero FFT.

Dnia 21.02.2022 o 11:28, FlyingDutch napisał:

Polecono mi użycia filtru dolnoprzepustowego, w szczególności filtru "wędrująca średnia" (moving Average), który jest stosunkowo prosty do implementacji.

Ok... czyli jeżeli w sygnale pojawi się uskok wynikający z użycia okna prostokątnego (czyli w zasadzie nieużywania okna) to ten filtr powinien to zniwelować. No dobra, ja nie jestem autorytetem, najwyraźniej tak się to robi :) 

2 godziny temu, FlyingDutch napisał:

Jeślibyśmy liczyli FFT z tych 2**14 (16384) sampli przychodzących z przetwornika ADC (ADS1256) to wykres widma mógłby być odświeżany 2 razy na sekundę, czy to by wystarczyło? Sama FFT będzie się liczyła bardzo szybko (w zależności od zastosowanego układu FPGA), ale czas zebrania 16384 sampli z prędkością 30Ksps będzie trwał około pół sekundy.

Myślałem o tym przy pisaniu kodu na ESP32. Chciałbym mieć odświeżanie wyników FFT 30Hz i w naiwnej wersji mojego kodu zbieranie próbek też było problemem. Więc wymyśliłem, że zrobię tablicę ringbuffer o długości 2x licza próbek i będę tam dodawał próbki jak leci, a odpalał przeliczanie na wycinku z zadanym krokiem.

Np bufor ma 16384 próbek, FFT potrzebuje połowę 8192, FS = 30ks/s = 30s/ms, docelowa częśtotliwość odświeżania 30Hz więc co 33ms trzeba odpalić FFT czyli w czasie jednego okresu 1/30Hz zbierze się 1000próbek. Czyli FFT wykonana się na 8192-1000 próbkach starych ale za to nie będzie ograniczeń w częstotliwości odświeżania.

Ja bym celował w FFT na 2048 próbkach, do tej pory miałem 512 i było ok. Pamiętaj że i tak to nie ma wielkiego znaczenia bo w tym urządzeniu próbki finalnie trafiają do kubełków żeby zawężyć je do 19 przedziałów (słupki wyświetlacza).

Do tworzenia indeksów kubełków użyłem własny kod w pythonie. Basy mają większą energię niż wysokie częstotliwości więc kubełkitrzeba uzupełniać wykładniczo - pierwsze kubełki mają sumę kilku wartości, ostatnie kubełki sumę setek. Tu masz przykład dnych wynikowych:

const int bands512[255] = {
0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 
10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 
14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18};

Kod na moim githubie:

def list_bonds(min_freq, max_freq, sampling_rate, samples_count, bands_count):
    bands = []

    bin_width = sampling_rate / samples_count
    print(f"Bin width: {round(bin_width, 3)} Hz")

    usable_bins = samples_count//2 - 1
    print(f"Usable bins: {usable_bins}")

    freq_multiplier = (max_freq/min_freq)**(1.0/(bands_count - 1))

    print(f"Frequency multiplier: {round(freq_multiplier, 3)}")

    center_bins = []
    for i_band in range(bands_count+1):
        band_freq = min_freq * (freq_multiplier) ** i_band
        center_bin = (band_freq / bin_width)
        center_bins.append({"i_band": i_band, "band_freq":band_freq, "center_bin":center_bin})
    # print(center_bins)

    min_max_bonds = []

    for i_band in range(bands_count):
        sel_bin = center_bins[i_band]["center_bin"]
        next_bin = center_bins[i_band+1]["center_bin"]
        low_bin = 0
        if i_band > 0:
            low_bin = max(int(round(min_max_bonds[i_band - 1]["high"])), 0)
        high_bin = min(int(round((next_bin-sel_bin)//2 + sel_bin)), usable_bins)
        if i_band == bands_count - 1:
            high_bin = usable_bins
        range_list = list(range(low_bin, high_bin))
        min_max_bonds.append({"low":low_bin, "high":high_bin, "range":range_list})
        print(f"{i_band}:  {low_bin}-{high_bin},  {range_list}")

    check_sum = 0
    for i, min_max_bond in enumerate(min_max_bonds):
        items_count = len(min_max_bond["range"])
        check_sum += items_count
        bands.extend([i]*items_count)

    print(f"Usable bins count ({check_sum}) correct: {check_sum == usable_bins}")


    return bands


bands = list_bonds(220, 18000, 40000, 512, 19)
# bands = list_bonds(260, 18000, 40000, 512, 19)

str_output = f"int bands[{len(bands)}]" + " = {\n"
file = open("bands_from_bins.txt", 'w')

for i, b in enumerate(bands):
    str_output += str(b) + ", "
    if i % 32 == 31:
        str_output += "\n"
str_output = str_output[0:-2]
str_output += "};\n"

file.write(str_output)

 

  • Pomogłeś! 1
Link do komentarza
Share on other sites

21 minut temu, Gieneq napisał:

Cześć @FlyingDutch dziękuję za zaangazowanie, dopiero jutro będę miał dostęp do komputra, ale cały czas uczę się Veriloga. Teraz przerabiam przykłady znalezione w internecie i trochę jestem zaskoczony, że do wszystkiego trzeba podchodzić ze sporym dystansem. W przypadku zwykłych językow programowania są jakieś prawdy oczywiste, a tu widzę, że jak zapoznałem się z taką tabelką wytycznych używania always @ 

image.thumb.png.17747b5bd0fd9a499d1ad34076565b78.png

to w dalszym przykładzie widzę brak konsekwencji...


always @(clk, currentState, count)
begin
    if(count<=x)
        currentState = continueState;
    else
        currentState = stopState;
end

sygnał x nie jest na liście a podobno powinien być, clk zazwyczaj miał warunek if(clk) tu nie ma nic. Podobno begin-end nie jest potrzebne dla takiego warunku. Ehh... Wyklaruje się kiedyś 🙂

Dlatego staram się porównywać przykłady, żeby jakoś wyważyć to czego się uczę.

.....
 

Zgadzam się z Tobą, trochę ostrzegałem, że Verilog jest mniej jednoznaczny niż VHDL (dlatego ja wolę VHDL). Ja po prostu zapamiętałem ,ze jeżeli nie używasz w warunku (event) posedge lub negedge i są przypisania blokujące to będzie prawdopodobnie wygenerowany układ kombinacyjny. Niestety języki HDL są dużo mniej jednoznaczne niż języki programowania, co gorsza ten sam kod w HDL na różnym sofcie (syntetyzer) może generować różny układ wynikowy. Co jeszcze gorsze, dwie różne syntezy dla tego samego kodu (i softu) mogą wygenerować dwa różne układy.

Bardzo dziękuję za podane przykłady i opisy - zaraz będę je analizował.

Pozdrawiam

Link do komentarza
Share on other sites

Cześć,

założyłem wczoraj wątek na forum EEVblog.com, gdzie przedstawiłem założenia odnośnie liczenia FFT i ogólnie ludzie sugerują, że w tym przypadku zastosowanie funkcji okna nie jest konieczne. poniżej link do tego wątku:

https://www.eevblog.com/forum/fpga/xilinx-fft-ip-core-window-filtering-function-is-not-implemented/

BTW: sam @hamster_nz mi odpowiedział i podał przykładowy kod w VHDL'u. Ten gość to legenda, przedtem próbowałem kilka z jego projektów na FPGA i dużo się z nich nauczyłem. Tutaj kod w VHDL'u (dla DFT 256 point):

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity dft_256 is
    Port ( clk        : in  STD_LOGIC;
           din        : in  STD_LOGIC_VECTOR (15 downto 0);
           din_valid  : in  STD_LOGIC;
           dout_r     : out STD_LOGIC_VECTOR (15 downto 0) := (others => '0');
           dout_i     : out STD_LOGIC_VECTOR (15 downto 0) := (others => '0');
           dout_valid : out STD_LOGIC                       := '0');
end dft_256;

architecture Behavioral of dft_256 is
    signal sequence_counter         : unsigned(7 downto 0) := (others => '1');
    signal sequence_counter_delay_1 : unsigned(7 downto 0) := (others => '1');
    signal sequence_counter_delay_2 : unsigned(7 downto 0) := (others => '1');
    signal sample_count             : unsigned(7 downto 0) := (others => '0');
   
    signal data_reg         : std_logic_vector(17 downto 0);
    type a_working is array(0 to 127) of std_logic_vector(35 downto 0);
    signal working_r        : a_working := (others => (others => '0'));
    signal working_i        : a_working := (others => (others => '0'));
    signal temp_r           : std_logic_vector(35 downto 0) := (others => '0');
    signal temp_i           : std_logic_vector(35 downto 0) := (others => '0');

    signal running_total_r  : std_logic_vector(35 downto 0) := (others => '0');
    signal running_total_i  : std_logic_vector(35 downto 0) := (others => '0');
    signal result_r         : std_logic_vector(35 downto 0) := (others => '0');
    signal result_i         : std_logic_vector(35 downto 0) := (others => '0');


    type a_sine_table is array(0 to 255) of std_logic_vector(15 downto 0);
    signal sine_table : a_sine_table := (
     x"0000", x"0192", x"0324", x"04B6", x"0647", x"07D9", x"096A", x"0AFB",
     x"0C8B", x"0E1B", x"0FAB", x"1139", x"12C7", x"1455", x"15E1", x"176D",
     x"18F8", x"1A82", x"1C0B", x"1D93", x"1F19", x"209F", x"2223", x"23A6",
     x"2527", x"26A7", x"2826", x"29A3", x"2B1E", x"2C98", x"2E10", x"2F86",
     x"30FB", x"326D", x"33DE", x"354D", x"36B9", x"3824", x"398C", x"3AF2",
     x"3C56", x"3DB7", x"3F16", x"4073", x"41CD", x"4325", x"447A", x"45CC",
     x"471C", x"4869", x"49B3", x"4AFA", x"4C3F", x"4D80", x"4EBF", x"4FFA",
     x"5133", x"5268", x"539A", x"54C9", x"55F4", x"571D", x"5842", x"5963",
     x"5A81", x"5B9C", x"5CB3", x"5DC6", x"5ED6", x"5FE2", x"60EB", x"61F0",
     x"62F1", x"63EE", x"64E7", x"65DD", x"66CE", x"67BC", x"68A5", x"698B",
     x"6A6C", x"6B4A", x"6C23", x"6CF8", x"6DC9", x"6E95", x"6F5E", x"7022",
     x"70E1", x"719D", x"7254", x"7306", x"73B5", x"745E", x"7503", x"75A4",
     x"7640", x"76D8", x"776B", x"77F9", x"7883", x"7908", x"7989", x"7A04",
     x"7A7C", x"7AEE", x"7B5C", x"7BC4", x"7C29", x"7C88", x"7CE2", x"7D38",
     x"7D89", x"7DD5", x"7E1C", x"7E5E", x"7E9C", x"7ED4", x"7F08", x"7F37",
     x"7F61", x"7F86", x"7FA6", x"7FC1", x"7FD7", x"7FE8", x"7FF5", x"7FFC",
     x"7FFF", x"7FFC", x"7FF5", x"7FE8", x"7FD7", x"7FC1", x"7FA6", x"7F86",
     x"7F61", x"7F37", x"7F08", x"7ED4", x"7E9C", x"7E5E", x"7E1C", x"7DD5",
     x"7D89", x"7D38", x"7CE2", x"7C88", x"7C29", x"7BC4", x"7B5C", x"7AEE",
     x"7A7C", x"7A04", x"7989", x"7908", x"7883", x"77F9", x"776B", x"76D8",
     x"7640", x"75A4", x"7503", x"745E", x"73B5", x"7306", x"7254", x"719D",
     x"70E1", x"7022", x"6F5E", x"6E95", x"6DC9", x"6CF8", x"6C23", x"6B4A",
     x"6A6C", x"698B", x"68A5", x"67BC", x"66CE", x"65DD", x"64E7", x"63EE",
     x"62F1", x"61F0", x"60EB", x"5FE2", x"5ED6", x"5DC6", x"5CB3", x"5B9C",
     x"5A81", x"5963", x"5842", x"571D", x"55F4", x"54C9", x"539A", x"5268",
     x"5133", x"4FFA", x"4EBF", x"4D80", x"4C3F", x"4AFA", x"49B3", x"4869",
     x"471C", x"45CC", x"447A", x"4325", x"41CD", x"4073", x"3F16", x"3DB7",
     x"3C56", x"3AF2", x"398C", x"3824", x"36B9", x"354D", x"33DE", x"326D",
     x"30FB", x"2F86", x"2E10", x"2C98", x"2B1E", x"29A3", x"2826", x"26A7",
     x"2527", x"23A6", x"2223", x"209F", x"1F19", x"1D93", x"1C0B", x"1A82",
     x"18F8", x"176D", x"15E1", x"1455", x"12C7", x"1139", x"0FAB", x"0E1B",
     x"0C8B", x"0AFB", x"096A", x"07D9", x"0647", x"04B6", x"0324", x"0192");

    signal trig_entry : unsigned(7 downto 0);
    signal sin_value  : std_logic_vector(15 downto 0);
    signal cos_value  : std_logic_vector(15 downto 0);
   
    component mac_block is
    port (
        clk : in std_logic;
        a : in std_logic_vector(17 downto 0);
        b : in std_logic_vector(15 downto 0);
        c : in std_logic_vector(35 downto 0);
        r : out std_logic_vector(35 downto 0)
    );
    end component;
begin

mac_block_r: mac_block port map (
    clk => clk,
    a   => data_reg,
    b   => cos_value,
    c   => running_total_r,
    r   => result_r);

mac_block_i: mac_block port map (
    clk => clk,
    a   => data_reg,
    b   => sin_value,
    c   => running_total_i,
    r   => result_i);
   
    -- For the first sample in a block set the running total to zero
    running_total_r <= (others => '0') when sample_count /= 0 else temp_r;
    running_total_i <= (others => '0') when sample_count /= 0 else temp_i;

process(clk)
    begin
        if rising_edge(clk) then
            -- Output the last completed DFT while inputting sample 0 of the next           
            if sample_count = 0 and sequence_counter >= 1 and sequence_counter < 128+1 then
                dout_r <= std_logic_vector(temp_r(35 downto 20));
                dout_i <= std_logic_vector(temp_i(35 downto 20));
                dout_valid <= '1';
            else
                dout_r <= (others => '0');
                dout_i <= (others => '0');
                dout_valid <= '0';
            end if;
           
            -- Write back any update value
            if sequence_counter >= 2 and sequence_counter < 128+2 then
                working_r(to_integer(sequence_counter_delay_2)) <= result_r;
                working_i(to_integer(sequence_counter_delay_2)) <= result_i;
            end if;
           
            -- Look up the working values and the sin/cos values
            if sequence_counter < working_r'high then
                temp_r    <= working_r(to_integer(sequence_counter));
                temp_i    <= working_i(to_integer(sequence_counter));
                sin_value <= sine_table(to_integer(trig_entry));
                cos_value <= sine_table(to_integer(trig_entry+64));
            end if;

            -- Restart the sequencer when a new sample arrives
            if din_valid = '1' then
                data_reg         <= din & "00";
                sequence_counter <= (others => '0');
                trig_entry       <= (others => '0');
                sample_count     <= sample_count + 1;
            elsif sequence_counter /= 255 then
                sequence_counter <= sequence_counter+1;
                trig_entry       <= trig_entry + sample_count;
            end if;
           
            -- Delayed sequence count for the write-back to working_r and working_i
            sequence_counter_delay_2 <= sequence_counter_delay_1;
            sequence_counter_delay_1 <= sequence_counter;
        end if;       
    end process;
end Behavioral;

próbowałem ten kod zsyntetyzować w "Gowin EDA" dla płytki "Sipeed Tang Nano 4K", ale otrzymałem błąd (FPGA na tej płytce ma 38 pinów I/O, a wymagane jest 51):

GowinEDA_err1.thumb.png.c299080ce4bc98ebb78cc290a595b6a5.png

Spróbuję zsyntetyzować ten kod na FPGA Xilinx'a (Spartan7 albo Artix7).

Zmieniłem kod skryptu w Pythonie do generacji sampli przebiegu audio - teraz generuję 2**13 - 8192 próbki. Do dwóch sinusoid dodałem przesunięcia fazy - patrz kod:

n = np.arange(8192) #2**13
sinus = 2*np.sin(2 * np.pi * n * 50 / fs)+3*np.sin(2 * np.pi * n * 200 / fs+1.5707)+1.8*np.sin(2 * np.pi * n * 1000 / fs)+0.9*np.sin(2 * np.pi * n * 6500 / fs+0.7853)+2.4*np.sin(2 * np.pi * n * 10000 / fs)+3.7*np.sin(2 * np.pi * n * 12400 / fs)+2*np.sin(2 * np.pi * n * 14800 / fs)
widmo = np.fft.fft(sinus)

Jednak Python to świetny język - programuje się w nim bardzo prosto i szybko. Dzięki instalacji modułu bibliotecznego "fxpmath" udało mi się w prosty sposób zamienić liczby float64 na zapis stało-pozycyjny (fixed-point) gdzie liczba ma 16 bitów (ze znakiem) i 6 bitów jest przed kropką (część całkowita) a 10 bitów w części ułamkowej. Przesiadłem się dzisiaj z "Jupyter Notebook" na darmowy "PyScripter" bo moduł "fxpmath" nie był dostępny dla Pythona w wersji 3.9, którego mam zainstalowanego z Anacond'ą.

Tutaj cały kod zmodyfikowanego skryptu Pythona (3.8.x):

#-------------------------------------------------------------------------------
# Name:        module1
# Purpose:
#
# Author:      mgabr
#
# Created:     23.02.2022
# Copyright:   (c) mgabr 2022
# Licence:     <your licence>
#-------------------------------------------------------------------------------

def main():
    pass

if __name__ == '__main__':
    main()

import numpy as np
import scipy.signal as sig
from scipy.io import wavfile
import matplotlib.pyplot as plt
from fxpmath import Fxp

# częstotliwość próbkowania
fs = 30000

n = np.arange(8192) #2**13
sinus = 2*np.sin(2 * np.pi * n * 50 / fs)+3*np.sin(2 * np.pi * n * 200 / fs+1.5707)+1.8*np.sin(2 * np.pi * n * 1000 / fs)+0.9*np.sin(2 * np.pi * n * 6500 / fs+0.7853)+2.4*np.sin(2 * np.pi * n * 10000 / fs)+3.7*np.sin(2 * np.pi * n * 12400 / fs)+2*np.sin(2 * np.pi * n * 14800 / fs)
widmo = np.fft.fft(sinus)

widmo_amp = np.abs(np.fft.rfft(sinus)) / 1024
f = np.fft.rfftfreq(8192, 1/fs)
plt.plot(f, widmo_amp)
plt.xlabel('częstotliwość [Hz]')
plt.ylabel('amplituda widma')
plt.title('Widmo "rzeczywiste" sygnału sinusoidalnego')
plt.show()

plt.plot(n, sinus)
plt.xlabel('czas [s]')
plt.ylabel('amplituda')
plt.title('Przebieg sygnału sinusoidalnego')
plt.show()

sinus2=sinus.reshape((1,8192)) #trzeba zmienic shape, aby zapis w pliku byl mozliwy
n2=n.reshape((1,8192))


# printing the array and checking datatype
print('Array:', sinus2.shape)
print('Datatype:', sinus2.dtype)

sinus2I=sinus2.astype(np.int64)
print('Array:', sinus2I.shape)
print('Datatype:', sinus2I.dtype)

file = open("C:\\Rob\\AudioSignal.txt", "w")

for x in range(0, 8191):
    z = Fxp(sinus2[0][x], signed=True, n_word=16, n_frac=10)
    file.write(z.bin()+"\n")
    #print(z.hex())   # hex repr
    #print(z.val)     # raw val (decimal of binary stored)

file.close()

Tutaj poczatek pilku .coe z którego można zainicjalizować pamięć ROM (IPCore Xilinx'a):

memory_initialization_radix=2;
memory_initialization_vector=
0000111010001011
0010000011100111
1111011110101011
0001110010011011
0000110000100111
0001010100111101
0001010001111001
0001000111011110
0001001101001010
0000100011001100
0010010110010010
0000011111001011
0000010010101001
0010010000000101
1111010010001000
0010000001001000
0000000011110100
0000001110010111
0000011010001100

A tutaj cały plik .coe z samplami (spakowany zipem):

AudioSignal.zip

Tutaj zrzut ekranu z "PyScriptera:

PyScripter01.thumb.png.97e51ac059a01240b3ae836a3ffef14a.png

Mając plik .coe mogę zainicjalizować pamięć ROM (IP Core Xilinx'a) i spróbować policzyć z tych 8192 próbek FFT (też IP Core Xilinx), a następnie porównać wyniki obliczonej transformaty z tymi z Pythona (które są poprawne). Zrobię to w "Vivado" na płytce FPGA ze Spartanem 7.

Pozdrawiam

Edytowano przez FlyingDutch
Link do komentarza
Share on other sites

Cześć @FlyingDutch

Ostatnie wydarzenia trochę osłabiły chęci do robienia czegokolwiek, ale nie ma co za dużo myśleć o rzeczach na które nie ma się wpływu, trzeba zająć się czymś konstruktywnym. 

Dnia 23.02.2022 o 08:51, FlyingDutch napisał:

założyłem wczoraj wątek na forum EEVblog.com

Właśnie szukałem informacji, o tym jak przeprowadzić symulację i trafiłem na jeden z Twoich wpisów 🙂 

image.thumb.png.cc905ea7f9b8302d85939c56f47dd909.png

Stronę bardziej kojarzę z YouTuba, gość świetnie tłumaczy.

Dnia 23.02.2022 o 08:51, FlyingDutch napisał:

Jednak Python to świetny język - programuje się w nim bardzo prosto i szybko.

Kiedyś miałem opory przed językiem, który nie ma jawnie podanych typów danych i jest jakiś taki chaotyczny, ale w tym chaosie można napisać bardzo elastyczny kod. W ubiegłym roku udzielałem korepetycji z Pythona m.in. studentce z Danii i sam się poduczyłem języka.

Dnia 23.02.2022 o 08:51, FlyingDutch napisał:

ale otrzymałem błąd (FPGA na tej płytce ma 38 pinów I/O, a wymagane jest 51):

Hm, a do czego to ma być potrzebne? Chyba dla tego, że sam moduł ma tyle wyprowadzeń, ale ostatecznie wyprowadzenia będą wewnętrznymi połączeniami z innymi modułami a nie IO.

Tak BTW. ciekawe czy te przetworniki dotrą do Polski, ajj... się sypnęło.

  • Lubię! 1
Link do komentarza
Share on other sites

Przed chwilą, Gieneq napisał:

Cześć @FlyingDutch

Ostatnie wydarzenia trochę osłabiły chęci do robienia czegokolwiek, ale nie ma co za dużo myśleć o rzeczach na które nie ma się wpływu, trzeba zająć się czymś konstruktywnym.

Kiedyś miałem opory przed językiem, który nie ma jawnie podanych typów danych i jest jakiś taki chaotyczny, ale w tym chaosie można napisać bardzo elastyczny kod. W ubiegłym roku udzielałem korepetycji z Pythona m.in. studentce z Danii i sam się poduczyłem języka.

Hm, a do czego to ma być potrzebne? Chyba dla tego, że sam moduł ma tyle wyprowadzeń, ale ostatecznie wyprowadzenia będą wewnętrznymi połączeniami z innymi modułami a nie IO.

Tak BTW. ciekawe czy te przetworniki dotrą do Polski, ajj... się sypnęło.

Cześć @Gieneq,

ja mam tak samo odechciało mi się robić cokolwiek, ale masz rację trzeba się otrząsnąć z szoku i zabrać za coś konstruktywnego. Masz rację z tymi wyprowadzeniami, ale można by napisać test-bench do zweryfikowania poprawności bez jakichś operacji obudowywania tego modułu.

Pozdrawiam

Link do komentarza
Share on other sites

(edytowany)

No tak, testbench by się przydał. Właśnie szukam czegoś do symulacji (na razie żeby w ogóle ruszyć z tematem). Prof. z YT używał iverilog, znalazłem poradnik i właśnie próbuję:

 

Edytowano przez Gieneq
Link do komentarza
Share on other sites

1 minutę temu, Gieneq napisał:

No tak, testbench by się przydał. Właśnie szukam czegoś do symulacji (na razie żeby w ogóle ruszyć z tematem). Prof. z YT używał iverilog, znalazłem poradnik i właśnie próbuję:

 

O ile pamiętam to używało się go z linii poleceń z podaniem dwóch nazw plików Verilog (moduł testowany i test_bench).

Pozdrawiam

Link do komentarza
Share on other sites

Faktycznie ciekawa sprawa, już mi się podoba 🙂 ma nawet przyzwoity debugger.

image.thumb.png.bee07d6cb3492064f5692cd6a47b4c2d.png

Może później napiszę osobny temat z instrukcją dla potomnych 🙂 

Link do komentarza
Share on other sites

Cześć,

ja jak na razie nie mogę się za bardzo skupić (po głowie krąży mnóstwo złych myśli), dlatego postanowiłem zrobić dzisiaj coś prostego. Mianowicie chciałem przetestować tworzenie pamięci ROM z wygenerowanego wczoraj za pomocą skryptu Pythona "coeficient file" (coe). Użyłem IP Core "Block Memory Generator" (Xilinx Vivado) na zestawie FPGA ze Spartanem 7. Patrz screenshot:

ROM_project01.thumb.png.f78579aef0764b977fba0ee87a91ad06.png

IP Core "ROM" (Na razie bez magistrali AXI) został obudowany bardzo prostym modułem top.v:

module top(input wire clk50Mhz,
           input wire[12:0] address,
           output wire[15:0] DOut);
  wire Enable;
    
  assign Enable = 1'b1;   
  
  blk_mem_gen_0 ROM (
  .clka(clk50Mhz),    // input wire clka
  .ena(Enable),      // input wire ena
  .addra(address),  // input wire [12 : 0] addra
  .douta(DOut)  // output wire [15 : 0] douta
);
endmodule

I napisałem w celu przetestowania poprawności tworzenia zawartości ROM z pliku coe prosty test_bench (test_top.v):

`timescale 1ns/10ps

module test_top();

reg clk;
reg[12:0] address;
wire[15:0] Out;

top DUT(clk, address, Out);

initial clk = 1'b0;
initial address=13'b0;

always #5 clk = ~clk;

initial
  begin
    $dumpfile("ROM.vcd");
    $dumpvars(0,test_top);
    $monitor($time,"Addr=%d, Data-%d",address, Out);
    #200 $finish;  
  end 

always @(posedge clk)
  begin
    address <= address+1;
  end

endmodule

Tutaj zrzut ekranu z wynikiem symulacji:

ROM_Simul.thumb.png.18ae76d6bc435fee6a5564c6803594db.png

Tutaj zamieszczam wcześniej wygenerowany plik AudioSignal.coe (spakowanny zip'em):

AudioSignal.zip

Można sobie porównać binarnie wartości pierwszych adresów ROM z zawartością tego pliku coe - wyniki są poprawne. Jutro umieszczę IP Core ROM z magistralą AXI, dzięki czemu będę mógł połączyć wyjście ROM z IP Corem "FFT" i spróbować policzyć na danych z ROM Transformatę Fouriera.

Pozdrawiam

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.