Skocz do zawartości

Pomocna odpowiedź

Napisano

Witam.
Mój post jest skierowany do wszystkich którzy są zainteresowani układami programowalnymi CPLD i FPGA.

Materiał powstał na podstawie własnych doświadczeń z układem Spartan3a i ma charakter praktycznych notatek.

Materiał jest dla początkujących. Nie opisuje wszystkich szczegółów języka VHDL ani architektury FPGA. Celem jest zrozumienie podstawowych pojęć potrzebnych do rozpoczęcia własnych projektów.

CPLD i FPGA są układami programowalnymi posiadającymi wewnętrzną strukturę logiczną, która umożliwia budowę zarówno prostych bramek logicznych, jak i złożonych układów cyfrowych.

Programowanie tego typu układów nie jest porównywalne z pisaniem kodów komputerowych, ponieważ zastosowanie programu konfiguracyjnego nie ma nic wspólnego z programem wykonywanym sekwencyjnie.

W opisie skupię się na układzie FPGA XILINX Spartan3a, który ma względnie zrozumiałą architekturę.

Układ ten posiada programowalne komórki SLICE złożone z dwóch komórek górna LUT4_G i dolna LUT4_F, multiplekser łączący komórki LUT4 o nazwie FMUX5. Komórki LUT odpowiadają głównie za logikę kombinatoryczną i w tym układzie posiadają (LUT_4G i F czyli 2 x 4 wejścia).

Komórka SLICE posiada również logikę szybkiego przeniesienia o nazwie CARRYCHAIN składającej się z MUXCY, XORCY, MULTAND. CARRY CHAIN służy do szybkiej propagacji przeniesienia w sumatorach i licznikach. i oszczędza zasoby LUT zostawiając miejsce na dodatkową logikę.

Komórka SLICE posiada również 2 przerzutniki typu D FlipFlop są nazwane REGISTER. Służą do tworzenia rejestrów dla danych i są 1 bitowe.

Komórka SLICE posiada również dodatkowe multipleksery umożliwiające łączenie funkcji logicznych budowanych z komórek LUT.

Komórki SLICE umieszczone są po 4 sztuki w bloku CLB. Bloki są wykorzystywane dla wewnętrznych połączeń między komórkami SLICE co redukuje dodatkowo czas propagacji sygnałów.

Dodatkowo układ FPGA posiada wbudowany BRAM który można konfigurować tylko o ściśle określonych wektorach danych i adresów by został przez syntezę rozpoznany. W innym przypadku niestandardowej długości wektorów synteza stworzy pamięć z komórek LUT.

Układ FPGA posiada również dedykowane układy mnożenia, DCM(Digital Clock Manager).

 

Schemat blokowy komórek SLICE:

                         image.thumb.png.33c1516f9f59f80fe29fda3cd9f9e641.png

Schemat blokowy komórek CLB:

                                   image.thumb.png.9622409ee93a5813b95f3e097a144043.png

Logika przykładowego układu spartan3a jest przedstawiona w celach poglądowych i w projektowaniu użytkownik pisze kod opisujący funkcję a wewnętrzne połączenia wykonuje syntezer wbudowany w narzędzie XILINX. Mimo to warto wiedzieć co ten układ potrafi by nie napisać kodu który wymusi zbyt długie połączenia lub sygnały które mogą się na siebie nakładać powodując błędne wyniki. Syntezer ma też algorytmy które nas poinformują o błędnym kodzie lub da ostrzeżenie o sygnałach które są niestabilne.
 

 

 

Język konfiguracji który omówię nosi nazwę VHDL ( ang. Very High Speed Integrated Circuit Hardware Description Language).

 

Program syntezy używa bibliotek które umożliwiają interpretację kodu i sygnałów.
Osobiście używam przeważnie takich bibliotek i na początek uważam je za wystarczające.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
Biblioteki deklaruje się na samym początku przed kodem.

Ta biblioteka jest wymagana praktycznie zawsze:
use IEEE.STD_LOGIC_1164.ALL;

Natomiast biblioteki poniżej są popularne w starszych projektach Xilinxa i Spartana-3A:
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

Ale w nowych projektach stosuje się częściej:
use IEEE.NUMERIC_STD.ALL;
 

Następnie deklarujemy porty we, wy lub we/wy

entity PIERWSZY is
Port (
A : in STD_LOGIC;
B : in STD_LOGIC;
Q : out STD_LOGIC;
D : inout STD_LOGIC;
S : out STD_LOGIC_VECTOR (7 downto 0)
);
end PIERWSZY;

entity ← deklaracja nazwy modułu
PIERWSZY ← nazwa modułu
Port ( ← po tym znaku wpisujemy wszystkie nazwy sygnałów które będą używane w celu połączenia z zewnętrznym środowiskiem

A ← nazwa sygnału.
: ← oddzielenie nazwy od deklaracji
in ← sygnał jest wejściowy
STD_LOGIC ← logika bitowa (standardowa jak w innych układach logicznych)


Przykład deklaracji wyjścia danych 1 bit:
B : out STD_LOGIC;
Dane ← nazwa sygnału.
: ← oddzielenie nazwy od deklaracji
out ← sygnał jest wyjściowy
STD_LOGIC ← logika bitowa (standardowa jak w innych układach logicznych)


Przykład deklaracji wejścia/wyjścia danych 1 bit:
D : inout STD_LOGIC;
D ← nazwa sygnału.
: ← oddzielenie nazwy od deklaracji
inout ← sygnał jest wejściowy i wyjściowy zależnie od konfiguracji w kodzie
STD_LOGIC ← logika bitowa (standardowa jak w innych układach logicznych)

Przykład deklaracji wyjścia danych 8 bit:
S : out STD_LOGIC_VECTOR (7 downto 0);
S <– nazwa sygnału.
: <– oddzielenie nazwy od deklaracji
out <– sygnał jest wyjściowy
STD_LOGIC_VECTOR <– logika bitowa wektorowa opisująca wektor bitowy (7 downto 0) czyli lewy bit nr. = 7 a prawy bit nr.= 0 (standardowa jak w innych układach logicznych)
zapis logiki wektorowej używamy gdy potrzebny jest zapis magistrali większej jak 1 bit by uniknąć wielu nazw dla każdego sygnału. W tym przypadku zapis (7 downto 0) można rozumieć liczby w zbiorze od 7 do 0 włącznie, i są dodawane jako kolejny numer do sygnału. W ten sposób synteza rozpoznaje zapis wielu sygnałów o tej samej nazwie odpowiednio je numerując na podstawie deklaracji zapisanej w wektorze.

 

Pod deklaracją sygnałów zewnętrznych możemy zadeklarować sygnały, które będą użyte tylko w tym module:

architecture Behavioral of PIERWSZY is

Następnie nadajemy nazwy sygnałom. W wewnętrznych sygnałach nie inicjujemy ich jako we lub wy ponieważ syntezer sam wyznaczy sposób ich implementacji w logice układu.

signal I : std_logic_vector (1 downto 0) ;
signal F : std_logic;

Po zapisaniu wszystkich sygnałów wpisujemy początek zapisu różnych funkcji.

Begin
 

W tym miejscu opisujemy konfigurację łącząc sygnały przez funkcje.

Możemy podłączyć funkcję bezpośrednio do sygnałów zewnętrznych.

Przykład funkcji „and”:

                                 image.png.e66944b1835c23908d145123684a12e2.png


Q <= A and B;

Q ← wyjście funkcji
<= ← znak przypisujący funkcję do sygnału
A ← wejście sygnału o nazwie A
B ← wejście sygnału o nazwie B
and ← funkcja logiczna

W ten sposób tworzy się dwu wejściową bramkę „and”.
Wszystkie funkcje przypisujemy za pomocą znaku <= do wybranych przez nas sygnałów.

Po zakończonym opisie deklarujemy koniec słowem „end”
np.:
end Behavioral;

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

Tak powinien wyglądać prawidłowo napisany moduł w języku VHDL z użyciem sygnałów zewnętrznych:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

 

entity PIERWSZY is

Port (

A : in STD_LOGIC;

B : in STD_LOGIC;

Q : out STD_LOGIC

);

end PIERWSZY;

 

architecture Behavioral of PIERWSZY is

 

begin

 

Q <= A and B;

 

end Behavioral;

 

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


Tak powinien wyglądać prawidłowo napisany moduł w języku VHDL z użyciem sygnałów wewnętrznych:


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity PIERWSZY is

Port (

A : in STD_LOGIC;

B : in STD_LOGIC;

Q : out STD_LOGIC

);

end PIERWSZY;

 

architecture Behavioral of PIERWSZY is

 

signal AB : std_logic_vector (1 downto 0);

signal F : std_logic;

 

begin
 

AB(1) <= A;

AB(0) <= B;
 

F <= AB(1) and AB(0);

 

Q <= F;

 

end Behavioral;

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

 

W drugim przykładzie przypisane są sygnały wejściowe zewnętrzne do sygnałów wewnętrznych, a sygnał F z funkcji and przypisałem do wyjścia Q.

 

Na pytanie dlaczego sygnały zewnętrzne i wewnętrzne, odpowiedź jest prosta.

Możemy mieć dużo modułów i je łączyć w większe bloki.

Wewnątrz każdego modułu mogą być używane różne sygnały które wykonują różne funkcje a my chcemy tylko wynik bez plątaniny połączeń wewnątrz. Stąd nazwa moduł.

Moduły są tworzone dla poprawy czytelności i przede wszystkim są pomocą dla korekty kodu, bez przepisywania całego bloku.

 

Plik UCF.

Jest to plik który łączy sygnały zewnętrzne modułu do fizycznych pinów w FPGA

 

Zapis w tym pliku wygląda następująco:

 

NET "A" LOC = "p3" ;

NET "B" LOC = "p4" ;

NET "Q" LOC = "p5" ;

 

NET ← Ścieżka sygnału zewnętrznego zadeklarowanego w module

"A" ← Nazwa sygnału zewnętrznego zadeklarowanego w module

LOC ← Przypisanie do fizycznego pinu. Można czytać ulokuj w...
"p3" ← można czytać dosłownie pin nr 3

Wstęp do programowania w języku VHDL jest długi i nie ma sensu uczyć się wszystkiego na pamięć. Opis nie jest dla nauki tylko służy jako informacja.

W następnych wpisach postaram się wyjaśnić jak opisywać różne struktury jak liczniki, sumatory, dekodery i inne elementy logiczne.

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...