Dlaczego MIPS zawiera shamt i rozróżnia funk / opcode?

15

Jestem zdezorientowany, dlaczego projektanci MIPS mieliby 5 bitów poświęconych przesunięciu i mieli osobne bity opcodu i funkcji.

Ponieważ MIPS jest tak RYZYKO, zakładam, że tylko kilka przesunięć można by wykonać w kilku instrukcjach, więc te 5 bitów wydaje się marnować miejsce, gdy można je natychmiast wprowadzić. Zakładam, że opcodes i FUN są osobne dla rozróżnienia instrukcji typu R i I, ale można to zrobić poprzez rozszerzenie kodu oprocentowania o 1 bit. Przy obu tych instrukcjach typu R może mieć długość 22 bitów. To nie zadziała, jeśli instrukcje typu I i typu J chcą zachować swój adres bezpośredni i adres, ale oba wydają się niepotrzebne.

qwr
źródło

Odpowiedzi:

10

Jest tu kilka różnych kompromisów.

Po pierwsze, chcemy, aby instrukcje miały stałą szerokość (32 bity). Gwarantuje to, że instrukcje są blokowane w pamięci podręcznej i wyrównane do strony, co upraszcza pamięć podręczną i obecność strony oraz kontrolę uprawnień.

Po drugie, chcemy, aby różne pola instrukcji ( opcode/ source regs/ immediates) miały stałą szerokość i stałą pozycję. To sprawia, że ​​dekodują je szybciej / mniej logicznie i są potrzebne na wczesnych etapach procesu. ( destinationRejestr nie jest potrzebny do końca potoku, więc może znajdować się w różnych miejscach Ri Iinstrukcjach.) Pozycja i szerokość functionpola mają znaczenie mniej ważne, ponieważ to musi kontrolować funkcję ALU, ale to jest w trzecim etapie rurociągu, więc masz trochę czasu, aby z nim popracować w razie potrzeby.

IJJ2)282)28Iinstrukcje są również przydatne dla autorów kompilatorów / linkerów. (Na SPARC, gdzie bezpośrednie pole miało tylko 12 bitów, musieli dodać całą specjalną load-highklasę instrukcji z 20-bitowym natychmiastowym.)

2)6=64JRI

Ale to pozostawia trochę poruszenia z Rinstrukcjami. Oprócz 6-bitowego kodu operacyjnego, potrzebują one tylko 15 dodatkowych bitów do specyfikacji rejestru, co pozostawia 11 bitów dla rozszerzonego kodu operacji i / lub kwoty przesunięcia.

Powinieneś pomyśleć o functionpolu jako o rozszerzonym kodzie operacji dla Rinstrukcji. Jest tylko jeden Rkod functionsoperacji Rinstrukcji , ale są 64 różne, które instrukcja może wykonać.

W porządku. Mamy 60 różnych Iinstrukcji i 64 różnych Rinstrukcji, więc gdzie powinniśmy umieścić instrukcje natychmiastowej zmiany?

Cóż, nie tylko jest mniej Iinstrukcji, ale jest o wiele więcej rzeczy, które chcemy zrobić z I instrukcjami. Przypomnij sobie, że wszystkie instrukcje gałęzi muszą być Iinstrukcjami, ponieważ mają względne (bezpośrednie) przesunięcie. Również wszystkie instrukcje ładowania i przechowywania są Iformatowane w MIPS. I wreszcie potrzebujemy instrukcji load-top-instant, która będzie Iinstrukcją. Nie tylko to, ale Rinstrukcje nadal mają 5 dodatkowych nieużywanych bitów (co jest nam potrzebne do natychmiastowego przejścia na natychmiastową zmianę w tej architekturze), więc daje to dodatkową zachętę do przekształcenia natychmiastowych zmian w specjalne (dziwne) Rinstrukcje .

Wiele z tych decyzji jest bardziej sztuką niż nauką, ale istnieje logika, którą można dostrzec. Kluczowym celem nie jest, aby liczba instrukcji była tak mała, jak to możliwe, lecz aby uzyskać wysoką wydajnośćdopasowanie potoku na jednym chipie (aby małe firmy, takie jak MIPS i Sun były w latach 80., mogły konkurować z IBM i DEC). (Nazwa RISC, wymyślona przez Davida Pattersona, jest nieco niefortunna. Przykuła uwagę, ponieważ była urocza, a nie dlatego, że „zredukowane instrukcje” są dokładnym opisem tego, co naprawdę próbowały zrobić architektury MIPS i SPARC.) Więc chcesz instrukcje o stałej szerokości (i stosunkowo małej, aby uzyskać lepsze zachowanie pamięci podręcznej I), aby pobieranie, stronicowanie i dekodowanie były prostsze i szybsze. Chcesz części instrukcji, które trzeba wcześniej zdekodować (opcode, dwa rejestry źródłowe i znak rozszerzony natychmiastowy) mają stałą szerokość i stałą pozycję. Chcesz, aby natychmiastowe elementy były tak długie, jak to możliwe, i chcesz mieć tyle różnych rodzajów instrukcji, ile będą pasować, biorąc pod uwagę wszystkie inne ograniczenia.

Wędrująca logika
źródło
Dziękujemy za twoją informacyjną odpowiedź, szczególnie część dotyczącą celów projektantów architektury. Interesujące jest porównanie MIPS z MOS 6502, ponieważ jeśli dobrze to rozumiem, 6502 nigdy nie miał shamtu (nadal próbuję zrozumieć formaty instrukcji).
qwr
1
6502 był konstrukcją mikroprocesora pierwszej generacji (pre-CISC), chociaż przewidywał potokowanie, ponieważ mógł wykonywać zapis zwrotny w tym samym czasie, gdy ładował następną instrukcję. 6502 miał bajtowe kody, jak większość 8-bitowych mikr. Inną architekturą do rozważenia jest ARM, który został zaprojektowany przez grupę inżynierów elektroniki wyższego poziomu, którzy czytali dokumenty RISC Berkeley i odwiedzili fabrykę MOS i zdecydowali „hej, możemy to zrobić”.
pseudonim
Zastanawiam się, jakie byłyby implikacje, gdyby istniał shamtowy wzorzec bitów, który oznaczał: „nie wykonuj poniższej instrukcji, ale użyj 32 bitów, które zostały dla niej pobrane, jako argument źródłowy tej instrukcji”? Alternatywnie lub dodatkowo zastanawiam się, czy praktycznym rozwiązaniem byłoby posiadanie sporego fragmentu kodu operacyjnego poświęconego parom prostych instrukcji nieprzerywalnych - koncepcja podobna do Kciuka, ale swobodnie przeplatana instrukcjami 32-bitowymi i bez możliwości przejść bezpośrednio do drugiej instrukcji słowa?
supercat
5

Aby zrozumieć formaty instrukcji MIPS I, musisz zrozumieć potok MIPS, a także wrócić do technologii implementacji procesora około 1985 roku. Jeśli spojrzysz na diagram (znasz ten), zobaczysz, że odczyt pliku rejestru znajduje się w Etap identyfikacji, zaraz po IF.

Do celów instrukcji typu R etap identyfikatora musi wykonywać następujące zadania:

  1. Ustal, że tak naprawdę jest to instrukcja typu R.
  2. Jeśli tak, powiedz plikowi rejestru, aby załadował wartości z rejestrów.

Na potrzeby tej dyskusji jest to pierwsze zadanie, o którym musisz pomyśleć. Jeśli jest dużo pracy z dekodowaniem instrukcji, którą musisz wykonać, aby nawet sprawdzić, czy potrzebujesz wartości z rejestrów, zwiększa to opóźnienie przed rozpoczęciem odczytu rejestrów. Zwiększa także złożoność etapu identyfikacji. Rezerwując jeden kod operacji dla wszystkich instrukcji typu R, ograniczasz złożoność do minimum.

Wydaje się trochę dziwne, że poświęcasz pięć bitów tylko przesunięciu. Mogę wymyślić kilka możliwych wyjaśnień. Jednym z nich jest to, że upraszcza routing (te pięć bitów ZAWSZE jest podawanych bezpośrednio do pliku rejestru, te pięć bitów ZAWSZE jest podawanych do manetki beczki, te sześć bitów ZAWSZE jest kierowane do ALU w celu ustalenia, która funkcja ma zostać wykonana).

Być może zastanawiali się nad wprowadzeniem w przyszłości połączonych instrukcji shift-left-and-add. Przypuszczalnie miałoby to postać:

$d = $s + ($t << shamt)

2)s+1s

Dzisiaj prawdopodobnie nie zastanawialibyśmy się dwa razy nad bardziej złożonym etapem dekodowania, zwłaszcza, że ​​dostęp do plików rejestrów zdarza się później w potoku typowego superskalarnego procesora. Wiele współczesnych procesorów wykonuje nawet zgrubne dekodowanie instrukcji w momencie wstawienia instrukcji do pamięci podręcznej L1 . Robisz, że linie pamięci podręcznej I są kilka bitów szersze, aby przechowywać dodatkowe informacje (dzięki prawu Moore'a masz wiele tranzystorów do stracenia), aby uprościć i przyspieszyć „właściwe” dekodowanie instrukcji.

Jednym z powodów, dla których prawdopodobnie chcieli utrzymać pole opcode tak małe, jak to możliwe, jest to, aby nie karało to nadmiernie instrukcji typu J. Jak zapewne wiesz, instrukcje typu J używają adresowania pseudo-bezpośredniego. Dla dobra każdego, kto gra w domu, krótko to wyjaśnię.

Pole adresowe instrukcji typu J ma 26 bitów. Ponieważ instrukcje są zawsze wyrównane do 4 bajtów, nie trzeba przechowywać najmniej znaczących dwóch bitów, co oznacza, że ​​efektywnie masz 28 bitów adresu. Jednak przestrzeń adresowa w MIPS I jest 32-bitowa. Zatem cztery górne bity lokalizacji skoku są pobierane z licznika programu.

Oznacza to, że nie można bezpośrednio przeskoczyć do lokalizacji, w której najważniejsze są cztery bity lokalizacji komputera. Zamiast tego musiałbyś wykonać droższy trzyczęściowy skok przez rejestr scratch:

lui $r,target >> 16
    ori $r,$r,target & 0xFFFF
    jr $r

Dzisiaj nie jest tak źle, ale w 1985 r. Jest dużo cykli zegara.

Kradzież trochę z pola adresu jeszcze bardziej zmniejszyłaby efektywny zasięg skoku bezpośredniego. Możesz zobaczyć, że może to być zbyt wysoka cena do zapłacenia.

Pseudonim
źródło
„połączone instrukcje shift-left-and-add” tego typu później widoczne w ARM?
Damian Yerrick