W wątku komentarza do odpowiedzi na to pytanie: Niepoprawne dane wyjściowe w jednostce VHDL stwierdzono:
„Dzięki liczbom całkowitym nie masz kontroli ani dostępu do wewnętrznej reprezentacji logiki w FPGA, podczas gdy SLV pozwala na takie sztuczki, jak efektywne wykorzystanie łańcucha przenoszenia”
Więc w jakich okolicznościach łatwiej jest kodować za pomocą wektora reprezentacji bitów niż za pomocą liczb całkowitych w celu uzyskania dostępu do wewnętrznej reprezentacji? A jakie zalety mierzyłeś (pod względem powierzchni chipa, częstotliwości zegara, opóźnienia lub innych).
Odpowiedzi:
Napisałem kod sugerowany przez dwa inne plakaty w obu
vector
iinteger
formie, dbając o to, aby obie wersje działały w podobny sposób, jak to możliwe.Porównałem wyniki w symulacji, a następnie zsyntetyzowałem za pomocą narzędzia Synplify Pro ukierunkowanego na Xilinx Spartan 6. Poniższe próbki kodu zostały wklejone z działającego kodu, więc powinieneś być w stanie używać ich z ulubionym syntezatorem i sprawdzić, czy zachowuje się tak samo.
Downcounters
Po pierwsze, downcounter, jak zasugerował David Kessner:
Architektura wektorowa:
Architektura liczb całkowitych
Wyniki
Pod względem kodowym liczba całkowita wydaje mi się lepsza, ponieważ pozwala uniknąć
to_unsigned()
połączeń. W przeciwnym razie nie wiele do wyboru.Uruchomienie go przez Synplify Pro z
top := 16#7fff_fffe#
produkuje 66 LUT dlavector
wersji i 64 LUT dlainteger
wersji. Obie wersje w dużym stopniu wykorzystują łańcuch nośny. Obie prędkości zegara przekraczają 280 MHz . Syntezator jest w stanie zapewnić dobre wykorzystanie łańcucha przenoszenia - wizualnie zweryfikowałem za pomocą przeglądarki RTL, że z podobną logiką powstaje oba te elementy. Oczywiście licznik dodatni z komparatorem będzie większy, ale to samo byłoby znowu z liczbami całkowitymi i wektorami.Dzielenie przez 2 ** n liczników
Sugerowane przez ajs410:
Architektura wektorowa
Architektura liczb całkowitych
Musisz skakać przez kilka obręczy, aby uniknąć użycia,
to_unsigned
a następnie zerwania kawałków, co wyraźnie dałoby taki sam efekt jak powyżej:Wyniki
Pod względem kodowym
vector
wersja jest w tym przypadku wyraźnie lepsza!Jeśli chodzi o wyniki syntezy, w tym niewielkim przykładzie wersja całkowita (jak przewidywano ajs410) wytwarza 3 dodatkowe LUT jako część komparatorów, byłem zbyt optymistyczny co do syntezatora, chociaż działa on z okropnie zaciemnionym fragmentem kodu!
Inne zastosowania
Wektory są wyraźną wygraną, gdy chcesz, aby arytmetyka się zawijała (liczniki można wykonać nawet jako pojedynczą linię):
vs
chociaż przynajmniej z tego kodu jasno wynika, że autor zamierzał zawinąć.
Coś, czego nie użyłem w kodzie rzeczywistym, ale zastanawiałem się:
Funkcja „owijania w sposób naturalny” może być również wykorzystana do „obliczeń poprzez przepełnienia”. Kiedy wiesz, że wynik łańcucha dodawania / odejmowania i mnożenia jest ograniczony, nie musisz przechowywać wysokich bitów obliczeń pośrednich, ponieważ (w uzupełnieniu 2-sekundowym) wyjdzie „w praniu” zanim dojdziesz do wyjścia. Powiedziano mi, że ten artykuł zawiera dowód na to, ale dokonanie szybkiej oceny wydawało mi się trochę gęste! Teoria dodawania i przepełnienia komputera - HL Garner
Użycie
integer
s w tej sytuacji spowodowałoby błędy symulacji po ich zawinięciu, nawet jeśli wiemy, że ostatecznie się rozpakują.I jak zauważył Philippe, kiedy potrzebujesz liczby większej niż 2 ** 31, nie masz wyboru, jak korzystać z wektorów.
źródło
variable c : unsigned(32 downto 0);
... nie jest więcc
zmienna 33-bitowa?Pisząc VHDL, zdecydowanie polecam użycie std_logic_vector (slv) zamiast liczby całkowitej (int) dla SIGNALS . (Z drugiej strony, użycie int dla ogólnych, niektóre stałe i niektóre zmienne mogą być bardzo przydatne.) Mówiąc prosto, jeśli deklarujesz sygnał typu int lub musisz określić zakres dla liczby całkowitej, prawdopodobnie robisz to coś źle.
Problem z int polega na tym, że programista VHDL nie ma pojęcia, czym jest wewnętrzna logiczna reprezentacja int, więc nie możemy z tego skorzystać. Na przykład, jeśli zdefiniuję liczbę całkowitą z zakresu od 1 do 10, nie mam pojęcia, jak kompilator koduje te wartości. Mam nadzieję, że będzie zakodowany jako 4 bity, ale nie wiemy zbyt wiele poza tym. Jeśli możesz sondować sygnały wewnątrz układu FPGA, może być zakodowany jako „0001” na „1010” lub zakodowany jako „0000” na „1001”. Możliwe jest również, że jest zakodowany w sposób, który nie ma dla nas żadnego sensu.
Zamiast tego powinniśmy po prostu użyć slv zamiast int, ponieważ wtedy mamy kontrolę nad kodowaniem, a także bezpośredni dostęp do poszczególnych bitów. Posiadanie bezpośredniego dostępu jest ważne, jak zobaczycie później.
Moglibyśmy po prostu rzucić int na slv, ilekroć potrzebujemy dostępu do poszczególnych bitów, ale robi się to naprawdę bałagan, bardzo szybko. To tak, jakby uzyskać najgorsze z obu światów zamiast najlepszych z obu światów. Twój kod będzie trudny do zoptymalizowania przez kompilator i prawie niemożliwy do odczytania. Nie polecam tego.
Tak więc, jak powiedziałem, przy pomocy slv masz kontrolę nad kodowaniem bitów i bezpośredni dostęp do bitów. Co możesz z tym zrobić? Pokażę ci kilka przykładów. Powiedzmy, że musisz wyprowadzać impuls raz na 4 294 000 000 zegarów. Oto jak zrobiłbyś to z int:
I ten sam kod przy użyciu slv:
Większość tego kodu jest identyczna między int i slv, przynajmniej w sensie wielkości i szybkości wynikowej logiki. Oczywiście jeden się odlicza, a drugi odlicza, ale to nie jest ważne w tym przykładzie.
Różnica polega na „ważnej linii”.
W przykładzie int spowoduje to 32-wejściowy komparator. W przypadku 4-wejściowych LUT, których używa Xilinx Spartan-3, będzie to wymagało 11 LUT i 3 poziomów logiki. Niektóre kompilatory mogą konwertować to na odejmowanie, które wykorzysta łańcuch przenoszenia i obejmie równowartość 32 LUT, ale może działać szybciej niż 3 poziomy logiki.
W przykładzie SLV nie ma porównania 32-bitowego, więc jest to „zero LUT, zero poziomów logiki”. Jedyną karą jest to, że nasz licznik to jeden dodatkowy bit. Ponieważ dodatkowe taktowanie dla tego dodatkowego bitu licznika znajduje się w łańcuchu przenoszenia, występuje dodatkowe „prawie zerowe” opóźnienie taktowania.
Oczywiście jest to skrajny przykład, ponieważ większość ludzi nie używałaby 32-bitowego licznika w ten sposób. Dotyczy to mniejszych liczników, ale różnica będzie mniej dramatyczna, choć nadal znacząca.
Jest to tylko jeden przykład wykorzystania slv w stosunku do int, aby uzyskać szybsze synchronizowanie. Istnieje wiele innych sposobów wykorzystania slv - potrzeba tylko trochę wyobraźni.
Aktualizacja: Dodano elementy, aby odpowiedzieć na komentarze Martina Thompsona dotyczące używania int z „if (count-1) <0”
(Uwaga: Zakładam, że miałeś na myśli „if count <0”, ponieważ dzięki temu byłby bardziej równoważny z moją wersją slv i usunąłby potrzebę dodatkowego odejmowania.)
W niektórych okolicznościach może to generować zamierzoną implementację logiki, ale nie gwarantuje się, że będzie działać przez cały czas. Będzie to zależeć od twojego kodu i tego, jak kompilator koduje wartość int.
W zależności od kompilatora i tego, jak określisz zakres wartości int, jest całkiem możliwe, że wartość int równa zero nie koduje wektora bitowego „0000 ... 0000”, kiedy przekształca się w logikę FPGA. Aby Twoja odmiana zadziałała, musi zakodować ją jako „0000 ... 0000”.
Załóżmy na przykład, że definiujesz wartość int w zakresie od -5 do +5. Oczekujesz, że wartość 0 zostanie zakodowana w 4 bitach, takich jak „0000”, i +5 jako „0101” i -5 jako „1011”. Jest to typowy schemat kodowania z uzupełnieniem dwójkowym.
Ale nie zakładaj, że kompilator będzie używał komplementu dwóch. Chociaż jest to niezwykłe, uzupełnienie jednego może skutkować „lepszą” logiką. Lub kompilator może zastosować rodzaj „stronniczego” kodowania, w którym -5 jest kodowane jako „0000”, 0 jako „0101”, a +5 jako „1010”.
Jeśli kodowanie int jest „poprawne”, kompilator prawdopodobnie wywnioskuje, co zrobić z bitem przeniesienia. Ale jeśli jest niepoprawny, wynikowa logika będzie okropna.
Możliwe, że użycie int w ten sposób może spowodować rozsądną wielkość logiczną i szybkość, ale nie jest to gwarancją. Przejście na inny kompilator (na przykład XST na Synopsis) lub przejście na inną architekturę FPGA może spowodować, że coś złego się stanie.
Unsigned / Signed vs. slv to kolejna debata. Możesz podziękować komitetowi rządu USA za udostępnienie nam tak wielu opcji w VHDL. :) Używam slv, ponieważ jest to standard interfejsu między modułami i rdzeniami. Poza tym i niektórymi innymi przypadkami symulacji, nie sądzę, aby korzystanie z SLV nad podpisanymi / niepodpisanymi było ogromną korzyścią. Nie jestem również pewien, czy podpisane / niepodpisane obsługują potrójne sygnały.
źródło
if (count-1) < 0
, pomyślałbym, że syntezator wyprowadzi bit przeprowadzania i wytworzy taki sam obwód jak twój przykład slv. Nie powinniśmy też używać tegounsigned
typu w dzisiejszych czasach :)Radzę wypróbować jedno i drugie, a następnie przejrzeć raporty podsumowujące, mapę i miejsca i trasy. Te raporty pokażą dokładnie, ile LUT zużywa każde podejście, a także maksymalną prędkość, z jaką logika może działać.
Zgadzam się z Davidem Kessnerem, że jesteś na łasce swojego łańcucha narzędzi i nie ma „właściwej” odpowiedzi. Synteza to czarna magia, a najlepszym sposobem, aby dowiedzieć się, co się stało, jest uważne i dokładne przeczytanie opracowanych raportów. Narzędzia Xilinx pozwalają nawet zajrzeć do wnętrza układu FPGA, aż do zaprogramowania każdej LUT, sposobu połączenia łańcucha nośnego, sposobu, w jaki tkanina przełączająca łączy wszystkie LUT itp.
Kolejny dramatyczny przykład podejścia pana Kessnera, wyobraź sobie, że chcesz mieć wiele częstotliwości taktowania na 1/2, 1/4, 1/8, 1/16 itd. Możesz użyć liczby całkowitej, która stale zlicza każdy cykl, a następnie mają wiele komparatorów w stosunku do tej liczby całkowitej, przy czym każde wyjście komparatora tworzy inny podział zegara. W zależności od liczby komparatorów fanout może stać się nadmiernie duży i zacząć zużywać dodatkowe LUT tylko do buforowania. Podejście SLV po prostu bierze każdy pojedynczy bit wektora jako wynik.
źródło
Jednym oczywistym powodem jest to, że podpisane i niepodpisane dopuszczają większe wartości niż 32-bitowa liczba całkowita. Jest to wada w konstrukcji języka VHDL, która nie jest niezbędna. Nowa wersja VHDL mogłaby to naprawić, wymagając wartości całkowitych do obsługi dowolnego rozmiaru (podobnie jak BigInt Javy).
Poza tym jestem bardzo zainteresowany usłyszeniem o testach porównawczych, które działają inaczej dla liczb całkowitych w porównaniu do wektorów.
BTW, Jan Decaluwe napisał fajny esej na ten temat: Te Ints są stworzone do Countin '
źródło