Wykład 7: Komunikacja z procesorem¶
Data: 08.12.2020
Treść
O rejestrach MMIO i szynach systemowych¶
Częścią praktycznie każdego bardziej skomplikowanego układu cyfrowego jest procesor (a często wiele procesorów), który steruje pracą całości bądź części układu. Procesor musi mieć możliwość komunikacji z każdą istotniejszą częścią takiego układu.
Istnieją dwa podejścia do podłączenia urządzeń do procesora:
ciasna integracja: urządzenie ma swój dedykowany interfejs i staje się w pewnym sensie częścią specyfikacji procesora (np. dedykowane instrukcje czy specjalne rejestry procesora do obsługi danego urządzenia)
luźna integracja: urządzenie jest podłączone do procesora przez standardową szynę (zazwyczaj współdzieloną z innymi urządzeniami) i jest widoczne dla procesora za pomocą zwykłych instrukcji dostępu do pamięci — pewna część fizycznej przestrzeni adresowej jest wydzielona dla urządzenia i odpowiada ono na instrukcje procesora które piszą bądź czytają ten obszar; podejście takie nazywa się MMIO (memory-mapped I/O)
Ciasna integracja jest używana rzadko (najczęściej gdy projektujemy jednocześnie procesor i jego urządzenia peryferyjne) i w przypadku procesorów ogólnego przeznaczenia używa się wyłącznie luźnej integracji.
Szyną systemową nazywamy zbiór połączeń, który łączy ze sobą procesory (i inne urządzenia będące w stanie inicjować transakcje) z urządzeniami peryferyjnymi. Może to być dość prosty bądź bardzo skompilokowany byt — może składać się z naprawdę wielu segmentów wykonanych w wielu technologiach, może być zawarta w ramach jednego układu scalonego, bądź też składać się z wielu układów i połączeń (być może kablowych).
Charakteryzuje się następującymi cechami:
szyna ma jednego lub więcej inicjatorów — urządzenia, które mogą inicjować transakcje na szynie; oczywistym przykładem inicjatora jest procesor, ale inicjatorami są też np. urządzenia peryferyjne będące w stanie wykonywać operacje bezpośredniego dostępu do pamięci (DMA)
szyna ma jeden bądź więcej targetów — urządzeń, które mogą odpowiadać na transakcje
główna pamięć systemu również jest targetem (pokrywającym duży zakres adrwsów)
standardowe transakcje na szynie to transakcje odczytu i zapisu (ale zdarzają się dziwniejsze typy transakcji na niektórych szynach); transakcje odczytu i zapisu składają się z co najmniej:
adresu docelowego (wysyłanego przez inicjatora)
rozmiaru (w bajtach) — jeśli jest większy niż 1, transakcja pokrywa wiele kolejnych adresów
danych (wysyłane przez inicjatora w przypadku zapisu, przez target w przypadku odczytu)
- — target dla danej transakcji jest wybierany na podstawie jej adresu — każdemu
targetowi jest przydzielony jakiś zakres (bądź zakresy) adresów; w ramach swoich zakresów target może dowolnie interpretować resztę adresu
mapowanie adresów na urządzenia może być kompletnie ustalone w momencie produkcji układu (w przypadku szyn mieszczących się w całości w jednym układzie) bądź mniej lub bardziej konfigurowalne (w przypadku bardziej skomplikowanych szyn, jak te w komputerach klasy PC)
choć zazwyczaj każdy inicjator na szynie widzi takie samo mapowanie adresów, nie zawsze jest to prawda — mogą istnieć targety niedostępne z części szyny
zdarzają się szyny z tzw. IOMMU — układami tłumaczącymi adresy; w takim wypadku, transakcje od niektórych inicjatorów przechodzą przez tłumaczenie adresów bardzo podobne do stronicowania przez procesor
szyna może składać się z wielu różnych pod-szyn wykonanych w różnych techonologiach i połączonych mostkami, bądź switchami
Układy SoC i AMBA¶
Układy SoC (system on a chip) to układy, które zawierają procesor wraz z dość kompletnym zestawem urządzeń peryferyjnych, pozwalając na złożenie kompletnego systemu z minimalną liczbą zewnętrznych układów (zazwyczaj tylko DRAM + flash). Taki układ jest sercem każdego współczesnego telefonu czy tableta. Takim układem jest również używany przez nas Zynq.
AMBA to zbiór standardów stworzonych przez ARM opisujących technologie komunikacji pomiędzy urządzeniami zawartymi w jednym układzie scalonym, w tym technologie służące do realizacji szyny systemowej. W skład AMBA wchodzą między innymi:
szyna AXI (Advanced eXtensible Interface) — główny interfejs używany przez współczesne duże procesory ARM i układy SoC na nich oparte; jest to interfejs point-to-point: komunikacja odbywa się pomiędzy jednym inicjatorem a jednym targetem i należy użyć switcha AXI, by móc spiąć ze sobą więcej urządzeń; jest oparty o 5 strumieni valid-ready i pozwala na jednoczesne wykonywanie wielu transakcji w potoku; ma wiele wersji:
AXI3: pierwsza wersja (używana na Zynq)
AXI4
AXI5
AXI4-Lite: uproszczone AXI4
AXI5-Lite
ACE (AXI Coherency Extensions): wersja AXI4 z dodatkowymi operacjami pozwalającymi na śledzenie stanu pamięci cache
ACE-Lite
ACE5: ACE, ale na bazie AXI5
szyna AHB (Advanced High-performance Bus) — poprzednik AXI, oparty o zupełnie inny schemat; jest to szyna wielodostępna (jest wielu inicjatorów, w każdym cyklu jeden z nich ma dostęp do szyny, wybrany przez arbitra), z wieloma targetami; używana w starszych (bądź mniejszych) procesorach ARM; istnieje też wariant AHB-Lite, w którym istnieje tylko jeden inicjator
szyna APB (Advanced Peripherial Bus) — dość prosty interfejs; ma jednego inicjatora i wiele targetów, nie ma żadnej możliwości przetwarzania potokowego ani równoległego; jest używana na „końcówkach” szyny systemowej, które nie wymagają zbyt dużej wydajności (czyli wolne urządzenia peryferyjne, bądź rzadko używane rejestry MMIO)
Zynq 7000¶
Zynq jest (poza częścią z FPGA) dość typowym układem typu SoC bazującym na procesorze ARM. Zawiera:
procesor ARM Cortex-A9
256kB wewnętrznego RAMu
kontroler pamięci DDR pozwalający na podłączenie do 1GB zewnętrznego RAMu
dość nietrywialną sieć szyn AXI i switchy stanawiącą szkielet szyny systemowej
kilka szyn APB, do których podłączone są układy peryferyjne
kilka szyn AXI łączących szynę systemową z FPGA i pozwalających na rozszerzenie jej przez układ użytkownika:
SAXI_HP[0-3]
: 4 szybkie i szerokie (64-bit) szyny AXI, w których FPGA jest inicjatorem, podłączone do switcha, który widzi tylko RAM (ten wbudowany i ten zewnętrzny) — pozwala na wydajny dostęp do pamięciSAXI_GP[0-1]
: 2 32-bitowe szyny AXI, w których FPGA jest inicjatorem, podłączone do switcha, z którego dostępna jest cała główna przestrzeń adresowa urządzenia — pozwala na dostęp do (prawie) wszystkich targetówSAXI_ACP
: 64-bitowa szyna AXI, w której FPGA jest inicjatorem, podłączone do kontrolera pamięci cache w procesorze — pozwala na dostęp do całej przestrzeni adresowej widzianej przez procesor w sposób spójny z jego cachem (pozostałe szynySAXI_*
pomijają cache)MAXI_GP[0-1]
: 2 32-bitowe szyny AXI, w których FPGA jest targetem; pozwala innym urządzeniom na wysyłanie transakcji do FPGA; szynaMAXI_GP0
ma przydzielony zakres adresów0x40000000:0x80000000
, a szynaMAXI_GP1
—0x80000000:0xc0000000
kilka układów peryferyjnych, które mają moce DMA i mają szynę AXI łączącą je (jako inicjator) z resztą systemu
dwie szyny AHB-Lite, którymi podłączone są do głównej sieci kontrolery SDIO (bo akurat takie kupili)
wolnostojący kontroler DMA, pozwalający na przesył danych między pamięcią a peryferiami (w przypadku Zynq, jedynym podłączonym urządzeniem peryferyjnym jest FPGA)
Szyna AXI¶
Pełny opis szyny AXI można znaleźć w dokumentacji: https://developer.arm.com/documentation/ihi0022/e/AMBA-AXI3-and-AXI4-Protocol-Specification?lang=en . Tutaj opowiemy tylko o najważniejszych elementach.
Szyna AXI3 (w wersji zaimplementowanej w Zynq) składa się z następujących połączeń:
sygnały sterujące:
ACLK
(zegar) iARESETN
(zanegowany reset), wspólne dla wszystkich 5 strumieni; w przypadku interfejsów FPGA na Zynq, są sterowane ze strony FPGA (Zynq zawiera odpowiedni mostek, który przeniesie dane z/do wewnętrznych domen zegarowych i możemy użyć dowolnego zegara nie większego niż 250 MHz)strumień żądań odczytu (od inicjatora do targetu) — służy do rozpoczynania transakcji odczytu:
ARVALID
,ARREADY
ARADDR
(32-bitowy): adres żądanego odczytuARID
(różnej długości): identyfikator inicjatora transakcji — pełny identyfikator inicjatora w całym Zynq ma 12 bitów, ale tylko część jest przydzielona do wyboru przez naszą szynę (pozostałe bity są wypełniane przez sieć połączeń i identyfikują port, z którego wysłaliśmy transakcję)MAXI_GP*
: 12 bitów (jako target widzimy cały identyfikator inicjatora)SAXI_ACP
: 3 bity (pozostałe bity identyfikatora wskazują na procesor, a wewnątrz procesora na port ACP)SAXI_GP*
: 6 bitów (pozostałe bity identyfikatora wskazują na naszą szynę)SAXI_HP*
: 6 bitów (pozostałe bity identyfikatora wskazują na naszą szynę)
ARBURST
(2-bitowy): typ transakcji:0:
FIXED
— wszystkie transfery w transakcji mają ten sam adres1:
INCR
— kolejne transfery w transakcji mają kolejno rosnące (o rozmiar transferu) adresy2:
WRAP
— dość skomplikowany tryb, podobny doINCR
, ale ze specjalnymi zasadami zawijania; używany do wczytywania całej linii cache zaczynając od wybranego słowa
ARSIZE
(2-bitowy): rozmiar jednego transferu danych, dekodowany następująco:0: 1 bajt
1: 2 bajty
2: 4 bajty
3: 8 bajtów (tylko na szynach 64-bitowych)
ARLEN
(4-bitowy): rozmiar transakcji w transferach pomniejszony o 1 (czyli 0 w tym polu oznacza 1 transfer; 5 oznacza 6 transferów)ARCACHE
(4-bitowy): atrybuty cacheowalności transakcji (można tu podać 0 jako inicjator)ARLOCK
(2-bitowy): atrybuty atomowości transakcji (należy tu podać 0 jako inicjator)ARPROT
(3-bitowy): atrybuty zabezpieczeń transakcji (można tu podać 0 jako inicjator)ARQOS
(4-bitowy): atrybuty priorytetu transakcji (można tu podać 0 jako inicjator)ARUSER
(5-bitowy, tylko naSAXI_ACP
): specjalne atrybuty transakcji (należy to podać 0 jako inicjator)
strumień danych odczytu (od targetu do inicjatora) — służy do przesyłania odczytanych danych w transakcji odczytu i kończenia transakcji odczytu:
RVALID
,RREADY
RDATA
(32-bitowe lub 64-bitowe): właściwe daneRID
(różnej długości): równeARID
odczytu, którego dane dotycząRLAST
(1-bitowe): prawda, jeśli to ostatni transfer danych z transakcji, kończy transakcjęRRESP
(2-bitowe): typ odpowiedzi:0:
OKAY
— udana transakcja1:
EXOKAY
— udana transakcja z wyłącznością2:
SLVERR
— transakcja odrzucona przez target3:
DECERR
— transakcja odrzucona przez szynę (adres nie odpowiada żadnemu targetowi)
strumień żądań zapisu (od inicjatora do targetu) — służy do rozpoczynania transakcji zapisu:
AWVALID
,AWREADY
AWADDR
(32-bitowy): adres żądanego zapisuAWID
(różnej długości): analogicznie doARID
AWBURST
(2-bitowy): analogicznie doARBURST
AWSIZE
(2-bitowy): analogicznie doARSIZE
AWLEN
(4-bitowy): analogicznie doARLEN
AWCACHE
(4-bitowy): analogicznie doARCACHE
AWLOCK
(2-bitowy): analogicznie doARLOCK
AWPROT
(3-bitowy): analogicznie doARPROT
AWQOS
(4-bitowy): analogicznie doARQOS
AWUSER
(5-bitowy, tylko naSAXI_ACP
): analogicznie doARUSER
strumień danych zapisu (od inicjatora do targetu) — służy do przesyłania zapisywanych danych w transakcji zapisu:
WVALID
,WREADY
WDATA
(32-bitowe lub 64-bitowe): właściwe daneWID
(różnej długości): równeARID
odczytu, którego dane dotycząWLAST
(1-bitowe): prawda, jeśli to ostatni transfer danych z transakcji, kończy transakcjęWSTRB
(4-bitowe dla 32-bitowej szyny, 8-bitowe dla 64-bitowej szyny): sygnały byte enable dla poszczególnych bajtów, wybierają które bajty w słowie faktycznie należy zapisać
strumień odpowiedzi na zapisy (od targetu do inicjatora) — kończy transakcję zapisu:
BVALID
,BREADY
BID
(różnej długości): identyfikator inicjatora transakcji — równeAWID
zapisu, na który odpowiadaBRESP
(2-bitowe): typ odpowiedzi, analogiczne doRRESP
Strumienie w AXI są wariantem opisanych wcześniej interfejsów valid-ready z następującymi wymaganiami:
gdy wysyłający ustawi sygnał valid na 1, nie wolno mu ustawić go na 0 ani zmienić pakietu dopóki pakiet nie zostanie zaakceptowany przez odbierającego (czyli gdy ready będzie równe 1) — nie wolno „zrezygnować” z przesyłu pakietu
nie wolno mieć żadnych ścieżek kombinacyjnych między żadną parą sygnałów w interfejsie
Zasady działania transakcji AXI są następujące:
Transakcja składa się z jednego lub więcej transferów, gdzie transfer ma dowolną długość będącą potęgą dwójki od 1 bajtu do szerokości szyny.
Bajt o adresie X jest zawsze transferowany na bitach
(X * 8 % DATA_WIDTH) : (X * 8 % DATA_WIDTH) + 8
szyny danych.Wszystkie adresy w ramach jednej transakcji muszą zawierać się w tym samym wyrównanym bloku 4kB.
Jeśli adres transakcji nie jest wyrównany do rozmiaru transferu, tylko część bajtów w pierwszym transferze zostanie przetransferowana; w pozostałych transferach zostanie już przetransferowany pełny rozmiar.
Targety będące pamięcią muszą wspierać wszystkie typy i rozmiary transakcji. Targety będące rejestrami MMIO mogą ograniczyć się do wybranego podzbioru.
Zawsze przesyłane jest dokładnie tyle danych, ile zostało wcześniej zadeklarowane (nawet w przypadku błędu).
Zapis wygląda następująco:
Inicjator wysyła na strumieniu
AW*
pakiet z adresem i metadanymi transakcji.Inicjator wysyła na strumieniu
W*
pakiety z danymi transakcji (tyle pakietów, ile jest transferów w transakcji). Ostatni pakiet w transakcji (i tylko on) ma ustawiony bitWLAST
. Przesył danych może odbywać się równolegle z przesyłem adresu.Wszystkie pakiety na strumieniu
W*
muszą być wysyłane w takiej samej kolejności co odpowiadające pakiety na strumieniuAW*
.Gdy target odbierze wszystkie pakiety odpowiadające jednej transakcji, wysyła na strumieniu
B*
jeden pakiet z odpowiedzią (nie wolno zrobić tego przed otrzymaniem wszystkich danych).Inicjator nie musi czekać na odpowiedź przed rozpoczęciem kolejnego zapisu — tempo wysyłania transakcji ograniczone jest tylko sygnałami gotowości targetu.
W ramach jednego identyfikatora
AWID/WID/BID
, odpowiedzi będą zawsze przychodzić w takiej samej kolejności, w jakiej zapisy zostały wysłane.Switchom i targetom wolno jednak dowolnie przestawiać zapisy o różnych identyfikatorach
AWID/WID/BID
— odpowiedzi mogą też przyjść w dowolnej kolejności.
Odczyt wygląda następująco:
Inicjator wysyła na strumieniu
AR*
pakiet z adresem i metadanymi transakcji.Target wysyła na strumieniu
R*
pakiety z danymi transakcji (tyle pakietów, ile ma być transferów w transakcji). Ostatni pakiet w transakcji (i tylko on) ma ustawiony bitRLAST
.Inicjator nie musi czekać na odpowiedź przed rozpoczęciem kolejnego odczytu — tempo wysyłania transakcji ograniczone jest tylko sygnałami gotowości targetu.
W ramach jednego identyfikatora
ARID/RID
, odpowiedzi będą zawsze przychodzić w takiej samej kolejności, w jakiej odczyty zostały wysłane.Switchom i targetom wolno jednak dowolnie przestawiać odczyty o różnych identyfikatorach
ARID/RID
— odpowiedzi mogą też przyjść w dowolnej kolejności.
Odczyty i zapisy są niezależnymi strumieniami i mogą być dowolnie przestawiane między sobą — jeśli inicjator chce, by miały ustaloną kolejność, powinien poczekać na odpowiedź na pierwszą transakcję przed wysłaniem drugiej.