WEBVTT

00:10.540 --> 00:13.180
Gut, würde ich sagen, wir fangen so langsam an.

00:16.540 --> 00:17.340
Könnt ihr mich hören?

00:18.680 --> 00:19.620
Gut, wunderbar.

00:22.460 --> 00:26.620
Ja, letzte Woche hatten wir angefangen, glaube ich, das grundlegende

00:26.620 --> 00:29.320
Rechnermodell uns anzuschauen oder zu entwickeln gemeinsam.

00:30.620 --> 00:33.900
Wir hatten eigentlich die Kernkomponenten, würde ich sagen, schon

00:33.900 --> 00:37.020
fertig gesehen und ein paar weitere Komponenten fehlen aber noch.

00:37.100 --> 00:40.240
Die gucken wir uns diese Woche noch an und dann haben wir quasi die

00:40.240 --> 00:43.260
erste, ja, grundlegende, so wie es Kapitel heißt, Rechnerarchitektur

00:43.260 --> 00:46.440
fertig und können uns danach anfangen, uns die ersten Techniken

00:46.440 --> 00:48.920
anzugucken, wie man das Ganze nicht nur funktional, sondern auch

00:48.920 --> 00:51.000
irgendwie ein bisschen schneller hinkriegen kann.

00:51.560 --> 00:53.920
Sprich, Pipelining werden wir uns als erstes halt eben anfangen

00:53.920 --> 00:54.680
anzugucken.

00:56.480 --> 01:01.520
Was wir auf jeden Fall gesehen hatten, war so die grundlegende

01:01.520 --> 01:04.620
Rechnerorganisation nach dem Von-Neumann-Prinzip, der Von-Neumann

01:04.620 --> 01:07.700
-Rechner, der quasi das Ganze aufteilt in sehr elementare

01:07.700 --> 01:08.720
Basiskomponenten.

01:08.720 --> 01:11.780
Das hatte damals gut funktioniert und genau genommen sieht jeder

01:11.780 --> 01:15.100
Rechner heute immer noch nach im Wesentlichen diesem Prinzip aus, was

01:15.100 --> 01:17.140
einfach daran liegt, dass es ziemlich allgemein gehalten war.

01:17.680 --> 01:20.880
Es gibt eine zentrale Einheit, auch Prozessor genannt, bestehend aus

01:20.880 --> 01:23.580
Steuerwerk und Rechenwerk und genau die beiden Teile, Steuerwerk und

01:23.580 --> 01:24.920
Rechenwerk, hatten wir uns auch schon angeschaut.

01:25.940 --> 01:29.700
Ein Verbindungsnetzwerk, dass das Ganze verbindet mit einmal einem Ein

01:29.700 --> 01:33.400
-Ausgabesystem nach außen zur Kommunikation und mit dem Hauptspeicher,

01:33.580 --> 01:36.260
wo dann quasi das Programm muss ja irgendwo liegen und die Daten,

01:36.260 --> 01:38.220
temporäre Daten müssen ja auch irgendwo liegen.

01:39.320 --> 01:40.780
Ja, also wirklich sehr allgemein gehalten.

01:42.480 --> 01:46.700
Ein bisschen konkreter oder, naja, deutlich konkreter, aber in einer

01:46.700 --> 01:49.640
relativ einfachen Architektur noch, hatten wir dieses Beispiel hier

01:49.640 --> 01:50.000
gesehen.

01:51.120 --> 01:52.460
Das gucken wir uns auch mal kurz an.

01:54.300 --> 01:56.360
Besteht halt eben, ja, was hatten wir gerade gesehen?

01:56.440 --> 02:00.100
Wir hatten gesehen, es gibt ein Steuerwerk, das hätten wir dann quasi

02:00.100 --> 02:02.280
hier in der Ecke.

02:02.960 --> 02:05.560
Es gibt ein Rechenwerk, hatten wir gesehen bei der von Neumann

02:05.560 --> 02:07.080
-Architektur, das wäre der Teil.

02:08.460 --> 02:11.360
Was man jetzt hier gar nicht so gut sehen kann, ist der Hauptspeicher,

02:11.460 --> 02:15.100
der ist quasi hier außen angeschlossen an Daten-Adressbus und

02:15.100 --> 02:20.020
Steuerbus und genau genommen ist das allgemein die Verbindung an, ich

02:20.020 --> 02:22.580
gehe mal kurz zurück, an dieses Verbindungsnetzwerk im Allgemeinen.

02:22.660 --> 02:26.300
Da kommt halt eben sowohl der Hauptspeicher als auch, wenn man gezielt

02:26.300 --> 02:29.860
auf die spezielle Adresse zugreift, würde man auch bei einer

02:29.860 --> 02:30.660
Peripherie landen.

02:30.800 --> 02:32.440
Also das ist beides hier unten.

02:34.080 --> 02:36.900
Jetzt haben wir noch zwei weitere, oder mehr eigentlich, weitere

02:36.900 --> 02:37.180
Blöcke.

02:37.480 --> 02:40.360
Wir haben zum einen hier ein Registersatz, also quasi ein interner

02:40.360 --> 02:44.840
Speicher, wo ja häufig benutzte Daten abgelegt werden können, auf die

02:44.840 --> 02:48.520
sehr schnell zugegriffen werden kann, im Vergleich zu allem, was hier

02:48.520 --> 02:52.220
außerhalb von der eigentlichen CPU ist und wir haben noch ein paar

02:52.220 --> 02:56.380
Register, auf die gehen wir gleich später noch in den neuen Themen

02:56.380 --> 03:00.680
ein, die quasi die Kommunikation ein bisschen abtrennen von dem

03:00.680 --> 03:01.480
Systembus.

03:02.020 --> 03:05.360
Also ein normaler Befehl würde zum Beispiel so aussehen, dass im

03:05.920 --> 03:10.340
Instruction Pointer, Program Counter, Befehlszähler drin steht, die

03:10.340 --> 03:14.020
Adresse von dem Befehl, die würde in den Adressbus Puffer gelegt

03:14.020 --> 03:17.200
werden, der würde die Adresse auf den Systembus legen, da würde der

03:17.200 --> 03:20.040
Speicher darauf ansprechen, der Speicher würde mit Daten antworten,

03:20.460 --> 03:27.160
die Daten sind in dem Fall der PC, ich meine der Befehl würde über den

03:27.160 --> 03:34.820
Systembus...

03:38.840 --> 03:46.140
Befehl würde über den Systembus erstmal ins Befehlsregister, das war

03:46.140 --> 03:49.100
glaube ich die Basis, ne das war der Befehlsregister, könnte mehrere

03:49.100 --> 03:52.780
Einträge groß sein, im Befehlsregister gelegt werden und dann wird die

03:52.780 --> 03:55.960
Steuerung, die Ausführung des Befehls übernehmen, in dem zum Beispiel

03:55.960 --> 03:59.360
Daten aus dem Registerfile über den Systembus an den

03:59.360 --> 04:01.520
Hilfsregistereingang von der ALU gelegt werden.

04:02.020 --> 04:06.840
Die ALU danach, dieses Registerwert, was hier rausgeholt worden ist,

04:07.520 --> 04:10.340
addiert zusammen mit irgendeinem Akkumulator, der hier vorher schon

04:10.340 --> 04:10.820
drin lag.

04:11.160 --> 04:14.220
Ergebnis könnte zum Beispiel wieder ins Registerfile gelegt werden

04:14.220 --> 04:16.920
oder es war vielleicht irgendeine Adressberechnung, da wird es ins

04:16.920 --> 04:22.000
Adresswerk gegeben, um dann irgendwie in dem Adresspuffer diesen

04:22.000 --> 04:26.040
Adresswert zu merken und dann auf den Adressbus zu legen, zum Beispiel

04:26.040 --> 04:28.660
für einen Speicherzugriff, würde halt eben auch die ALU benutzt

04:28.660 --> 04:29.860
werden, um eine Adressberechnung zu machen.

04:30.000 --> 04:32.200
Also je nachdem, was der Befehl tun muss, würde halt eben die

04:32.200 --> 04:34.580
Ausführung entsprechend gesteuert werden.

04:36.260 --> 04:39.400
Ja, also wie gesagt, Steuerwerk und Rechenwerk hatten wir uns letzte

04:39.400 --> 04:42.440
Woche schon fertig angeguckt, wir hatten da verschiedene Varianten

04:42.440 --> 04:45.200
gesehen, wie es so innen drin aussehen kann, aber ein paar weitere

04:45.200 --> 04:48.300
Sachen gibt es halt eben auch noch und die gucken wir uns heute noch

04:48.300 --> 04:48.760
fertig an.

04:50.400 --> 04:55.440
Gut, ja, eigentlich heißt das Kapitel Registersatz und passt der Stack

04:55.440 --> 04:58.900
eigentlich nicht so richtig gut zu, aber für einen Spezialfall wird

04:58.900 --> 04:59.920
tatsächlich auch mal ein Stack benutzt.

05:00.280 --> 05:01.840
Aber gucken wir uns einfach mal den Stack an.

05:01.860 --> 05:07.420
Stack, Keller, Speicher, Last-In, First-Out, LIFO, das heißt, ich habe

05:07.420 --> 05:10.620
nur zwei Operationen, mit denen ich darauf zugreifen kann, immer nur

05:10.620 --> 05:15.460
auf das oberste Element von dem Stack und das oberste Element ist halt

05:15.460 --> 05:18.660
eben das Element, was als letztes hinzugefügt wurde, also wenn ich

05:18.660 --> 05:22.480
quasi neues Element hinzufüge, ist das neue oberste Element und wenn

05:22.480 --> 05:25.220
ich eins entfernen will, dann ist das das, was zuletzt hinzugefügt

05:25.220 --> 05:25.820
worden ist.

05:26.700 --> 05:31.700
Das wird zum Beispiel benutzt für das Abspeichern von Programmzähler

05:31.700 --> 05:34.580
bei Unterprogrammen aufrufen, also man muss sich ja merken, wo kam ich

05:34.580 --> 05:37.760
her, also wenn die unter das Unterprogramm abgearbeitet ist, muss ich

05:37.760 --> 05:41.360
ja dahin zurück von wo das Unterprogramm aufgerufen worden ist, dafür

05:41.360 --> 05:44.660
muss ich mir merken, wo das war, dafür lege ich den alten Programme

05:44.660 --> 05:48.880
-Counter auf dem Stack ab, genau genommen nicht den alten Programme

05:48.880 --> 05:52.580
-Counter, sondern nicht den Befehl, den den Funktionsaufruf gemacht

05:52.580 --> 05:55.920
hat, sondern den Befehl danach, also dahin, wo ich wieder zurück will,

05:55.980 --> 05:58.640
was dann nach dem Funktionsaufruf als erstes passieren soll.

05:59.060 --> 06:01.920
Den lege ich auf dem Stack ab und kann danach dann hier wieder runter

06:01.920 --> 06:02.260
holen.

06:02.660 --> 06:05.320
Oder das Stack wird auch benutzt für Parameterübergabe, auch wieder

06:05.320 --> 06:06.780
beim Funktionsaufruf.

06:11.910 --> 06:15.430
Und um den Zugriff halbwegs schnell zu machen, gibt es mehr oder

06:15.430 --> 06:18.450
weniger ausgeprägten Hardware-Support, das Mindeste, was es mal gibt,

06:18.550 --> 06:21.490
ist, dass es irgendein Stack-Register gibt, wo gemerkt wird, also ein

06:21.490 --> 06:25.050
spezielles Register, wo der Stack-Pointer, das Top-of-Stack, das

06:25.050 --> 06:28.710
oberste Element, also ein Zeiger auf, ja, Zeiger auf das oberste

06:28.710 --> 06:31.690
Element abgelegt wird, also hier auch Stack-Pointer.

06:33.450 --> 06:35.470
Und wie gesagt, Stack kann nur auf zwei verschiedenen Arten

06:35.470 --> 06:38.750
zugegriffen werden, Push oder Pop, auch Pool genannt, um halt eben

06:38.750 --> 06:41.070
neues Element hinzuzufügen oder letztes Element wieder runter zu

06:41.070 --> 06:41.330
holen.

06:42.850 --> 06:45.570
Hier waren wir stehen geblieben, die Folie hatte ich kurz gezeigt,

06:45.710 --> 06:48.270
aber nicht mehr besprochen bekommen, wir gucken uns die jetzt noch mal

06:48.270 --> 06:48.470
an.

06:48.850 --> 06:52.370
Also wir gucken uns einfach die beiden Operationen an, man kann ahnen,

06:52.470 --> 06:53.390
wie sie funktionieren müssen.

06:53.490 --> 06:56.350
Es gibt so eine kleine Sache, auf die man achten muss, wie

06:56.350 --> 06:59.950
funktioniert quasi, wie funktioniert der Zugriff, die Arbeitsweise

06:59.950 --> 07:00.470
eines Stacks.

07:01.030 --> 07:04.110
Wir haben hier einen Arbeitsspeicher mit irgendwelchen Adressen, hier

07:04.110 --> 07:06.370
sind größere Adressen, hier sind kleinere Adressen und wir werden

07:06.370 --> 07:08.450
offensichtlich irgendwo hier in der Gegend von dieser Adresse

07:08.450 --> 07:10.090
irgendeine Operation machen.

07:11.950 --> 07:17.050
Erste Operation wäre quasi die Aufgabe, ich möchte einen Operanten

07:17.050 --> 07:19.610
schreiben in die Speicherzelle N.

07:19.750 --> 07:23.530
Also das ist jetzt nicht eine normale Stack-Operation, ich meine, bei

07:23.530 --> 07:25.670
der normalen Stack-Operation würde ich halt nicht sagen in

07:25.670 --> 07:28.470
Speicherzelle N, ich würde einfach sagen Push.

07:28.930 --> 07:33.630
Und hier ist das Beispiel so gewählt, dass das Element, wo der Operant

07:33.630 --> 07:37.950
landen wird, das ist gerade zufällig Adresse N, um dem Kind mal einen

07:37.950 --> 07:39.690
Namen zu geben, dass man es am Beispiel zeigen kann.

07:40.110 --> 07:43.670
Aber der Befehl wäre quasi einfach nur Push und Wert und das landet

07:43.670 --> 07:47.110
dann irgendwo und jetzt in diesem Beispiel ist gerade Speicheradresse

07:47.110 --> 07:50.330
N derjenige, wo es landen wird.

07:50.890 --> 07:53.410
Jetzt haben wir hier zwei verschiedene Situationen, einmal vor dem

07:53.410 --> 07:55.530
Befehl und einmal irgendwie was längeres.

07:56.070 --> 08:00.710
Vor dem Befehl zeigt der Stack-Pointer auf Adresse N plus 1, dann kann

08:00.710 --> 08:03.170
man jetzt schon mal sehen, dass in diesem Beispiel der Stack quasi

08:03.170 --> 08:06.250
nach unten wächst, also zu kleiner werdenden Adressen.

08:06.690 --> 08:09.170
Das ist nicht ungewöhnlich, das ist egal, also das ist eine von denen

08:09.170 --> 08:11.110
man kann es nach oben oder nach unten wachsen lassen, geht beides.

08:12.230 --> 08:17.570
Häufig ist es so, dass quasi das Programm, die Anwendung, die man hat,

08:17.830 --> 08:21.290
an Adresse 0 anfängt und dann quasi nach oben wächst und damit man

08:21.290 --> 08:24.430
sich nicht in die Quere kommt, fängt der Stack dann quasi ganz oben im

08:24.430 --> 08:25.970
Speicherbereich an und wächst nach unten.

08:26.370 --> 08:28.350
Und irgendwann treffen sich in der Mitte und wenn das passiert, hat

08:28.350 --> 08:29.530
man nicht genug Speicher im System.

08:30.210 --> 08:32.310
Also das ist nicht ungewöhnlich, dass Stacks nach unten wachsen, aber

08:32.310 --> 08:33.590
das ist reine Definitionssache.

08:34.110 --> 08:39.210
Also der Stack-Pointer vor dem Befehl zeigte auf Adresse N plus 1

08:39.210 --> 08:46.030
hier, dann kam Push und jetzt steht hier zwei Sachen und zwar einmal

08:46.030 --> 08:50.470
wird ja gemacht ein Speicherzugriff, in dem Fall Push, ich will was

08:50.470 --> 08:57.730
ablegen auf dem Stack und danach muss der Stack-Pointer noch

08:57.730 --> 08:58.670
modifiziert werden.

08:59.410 --> 09:03.610
Und hier ist es jetzt halt eben so, dass quasi zuerst der Stack

09:03.610 --> 09:08.490
-Pointer modifiziert wird, auf die Adresse N zeigt, also war in N plus

09:08.490 --> 09:12.150
1 offenbar schon was drin, N plus 1 war der Top of Stack, da können

09:12.150 --> 09:13.430
wir nicht reinschreiben, da ist schon was.

09:13.870 --> 09:17.770
Es wird zuerst der Stack-Pointer auf Adresse N verändert, danach wird

09:17.770 --> 09:22.170
der Speicherzugriff gemacht und das dann auch der Zustand, nachdem der

09:22.170 --> 09:23.030
Befehl fertig ist.

09:23.750 --> 09:26.870
Also quasi danach zeigt der Stack-Pointer auch auf diese Adresse hier.

09:27.550 --> 09:31.550
Wichtig ist diese beiden Sachen, Speicherzugriff und Pointer

09:31.550 --> 09:34.190
modifizieren, kann man in der einen Reihenfolge machen oder der

09:34.190 --> 09:34.430
anderen.

09:34.810 --> 09:38.750
Hier wird jetzt quasi erst den Pointer modifiziert und danach den

09:38.750 --> 09:39.730
Speicherzugriff gemacht.

09:40.270 --> 09:44.830
Wenn wir es jetzt wieder runter holen wollen mit Pop oder Pull, dann

09:44.830 --> 09:47.070
haben wir prinzipiell genau das gleiche zu tun.

09:47.170 --> 09:49.750
Wir müssten den Stack-Pointer modifizieren, ist ja ein Element weniger

09:49.750 --> 09:52.270
danach drauf und wir müssen den Speicherzugriff machen, in dem

09:52.270 --> 09:53.810
Verhältnis zum Lesen des Speicherzugriffs.

09:54.950 --> 09:56.270
Jetzt ist es gerade umgekehrt.

09:56.450 --> 10:01.310
Jetzt muss ich, ich meine, der Stack-Pointer zeigt auf Adresse N, so

10:01.310 --> 10:04.490
haben wir ihn nach dem Befehl hier zurückgelassen und er zeigt auf Top

10:04.490 --> 10:06.650
of Stack auf das oberste Element im Stack.

10:07.050 --> 10:10.530
Jetzt darf ich nicht zuerst den Stack-Pointer modifizieren, also

10:10.530 --> 10:13.030
hierhin zeigen und danach zugreifen, dann würde ich das falsche

10:13.030 --> 10:13.610
Element lesen.

10:13.910 --> 10:15.230
Es muss die Reihenfolge umgekehrt sein.

10:15.330 --> 10:18.970
Ich muss erst den Lesezugriff machen, damit bekomme ich Element N

10:18.970 --> 10:23.610
gelesen und danach den Stack-Pointer modifizieren.

10:24.190 --> 10:26.850
Also gerade umgekehrt wie beim Push-Befehl.

10:28.230 --> 10:31.710
Man hätte es gerade auf beiden Seiten bei Push und bei Pop umgekehrt

10:31.710 --> 10:34.650
machen können, das ist okay, geht auch, ist reine Definitionssache.

10:35.230 --> 10:38.550
Wichtig ist nur, wenn der Push es auf die eine Art macht, also in dem

10:38.550 --> 10:43.290
Fall hier erst Pointer modifizieren, danach zugreifen, dann muss er es

10:43.290 --> 10:45.810
gerade umgekehrt machen, um das richtige Element zu erwischen.

10:46.110 --> 10:51.090
Das ist quasi die Frage, ob der Stack-Pointer auf den Top of Stack

10:51.090 --> 10:54.430
zeigt, auf das oberste Element, wo wirklich schon Daten drinsteht,

10:54.810 --> 10:58.770
oder ob der Stack-Pointer auf das erste freie Element zeigt, wo die

10:58.770 --> 10:59.750
neuen Daten hin sollen.

10:59.970 --> 11:02.890
Das ist gerade Definitionssache.

11:03.210 --> 11:04.550
Also die beiden Möglichkeiten gibt es.

11:05.170 --> 11:07.490
Das Ganze steht hier im Wesentlichen noch mal, ich habe einen Stack

11:07.490 --> 11:09.770
-Pointer, ich habe einen zugreifenden Speicher, die beiden Sachen muss

11:09.770 --> 11:13.010
ich machen in einer gewissen Reihenfolge, kann ich mir frei aussuchen,

11:13.190 --> 11:15.970
aber man muss es halt mal festlegen und Push und Pop machen jeweils

11:15.970 --> 11:17.850
das Entgegensätze der beiden Reihenfolgen.

11:18.250 --> 11:21.610
Und das dann gerade die Variante, der Stack-Pointer erstes freies oder

11:21.610 --> 11:24.370
auch das letzte benutzte Element zeigt und eine andere Variante ist

11:24.370 --> 11:26.990
halt eben noch, der Stack nach oben oder nach unten wächst, also der

11:26.990 --> 11:32.070
Pointer dekrementiert wird oder inkrementiert wird beim einen der

11:32.070 --> 11:32.450
Zugriffe.

11:34.950 --> 11:38.390
Ja, jetzt kam über das Thema Registersatz irgendwie gar nichts vor.

11:38.490 --> 11:40.410
Das liegt im Wesentlichen daran, dass man über das Registersatz nicht

11:40.410 --> 11:41.510
so wahnsinnig viel sagen kann.

11:41.590 --> 11:43.930
Wir hatten schon gesehen, es ist irgendwie ein SRAM-Array mit

11:43.930 --> 11:50.070
irgendwie so Werten drin, also Register und man hat mehr oder weniger

11:50.070 --> 11:53.910
viele Lese- und oder Schreibports und viel mehr gibt es eigentlich gar

11:53.910 --> 11:54.410
nicht zu sagen.

11:56.390 --> 11:58.170
Nächstes ist jetzt noch das Adresswerk.

11:59.310 --> 12:02.950
Wir hatten ja schon beliebig viele verschiedene Adressberechnungen

12:02.950 --> 12:07.430
gesehen, die wiederholen wir glücklicherweise nicht nochmal, sondern

12:07.430 --> 12:10.690
hier steht einfach nur, das ist das Ding, was die Adressberechnung

12:10.690 --> 12:11.310
durchführt.

12:13.190 --> 12:17.170
Je nachdem, was für eine Adressberechnung es ist, muss man dafür die

12:17.170 --> 12:20.610
Adresse aus dem Buffet, aus dem Operand, aus dem Register rausholen

12:20.610 --> 12:22.330
und muss daraus irgendwas berechnen.

12:23.030 --> 12:26.950
Früher gab es das häufig nicht als dedizierten Block, sondern das war

12:26.950 --> 12:30.210
quasi, wenn ich sowieso eine ALU habe, dann kann ich die, ich meine,

12:30.290 --> 12:32.690
die kann rechnen, dann kann ich die ja auch für die Adressberechnung

12:32.690 --> 12:33.170
benutzen.

12:33.630 --> 12:35.990
Bis die Leute halt angefangen haben, immer mehr und mehr und mehr

12:35.990 --> 12:39.750
verschiedene, komplexere Adressberechnungen sich auszudenken, haben

12:39.750 --> 12:42.210
sie irgendwann dann auch als Reaktion darauf angefangen, ein

12:42.210 --> 12:46.250
dediziertes Adresswerk beizustellen, so dass man eine normale

12:46.250 --> 12:50.050
Programmberechnung parallel zu einer Adressberechnung durchführen

12:50.050 --> 12:50.290
kann.

12:54.960 --> 12:56.560
Wie könnte so ein Adresswerk aussehen?

12:56.740 --> 13:00.120
Einfaches Beispiel, das wäre quasi das Bild von vorhin mit der

13:00.120 --> 13:04.520
Verbindung an den Systembus unten und hier oben Adresswerk ein

13:04.520 --> 13:05.740
bisschen raus gesumt.

13:06.140 --> 13:08.380
Wir haben quasi eine ALU drin, wir müssen irgendwie rechnen können,

13:08.720 --> 13:11.000
wir haben wieder ein Hilfsregister davor, genau wie bei der anderen

13:11.000 --> 13:11.440
ALU.

13:11.900 --> 13:14.940
Was man häufig noch hat, ist irgendeine Form von Skalierung, also das

13:14.940 --> 13:18.840
soll heißen, dass was auch immer hier als Input reinkommt, mal 1

13:18.840 --> 13:22.020
genommen werden kann, also lassen wie es ist, oder mal 2, mal 4, mal

13:22.020 --> 13:24.320
8, also einfache Shift-Operationen.

13:25.120 --> 13:27.000
Wofür habe ich diese Skalierungen vorne dran?

13:27.520 --> 13:32.320
Das ist zum Beispiel dafür, wenn ich auf einen Program-Counter, also

13:32.320 --> 13:36.260
auf einen Befehl, zeige, dann beginnt ja quasi, wenn der Befehl ein

13:36.260 --> 13:41.760
Wort lang ist, beginnt ja nur alle vier Byte neuer Befehl.

13:41.980 --> 13:45.820
Der Speicher ist byte-adressiert, also der nächste Befehl ist quasi

13:45.820 --> 13:47.780
vier Byte weiter.

13:48.100 --> 13:52.100
Wenn ich sage, ich will sieben Befehle weiterspringen, irgendwo ein

13:52.100 --> 13:55.280
Sprungbefehl, dann muss ich irgendwie aus den sieben Befehlen weiter

13:55.280 --> 13:56.660
die Byte-Adresse ausrechnen.

13:57.000 --> 13:59.280
Und das halt eben gerade, indem ich die sieben mal vier nehme, dann

13:59.280 --> 14:01.640
weiß ich, wie viel Byte weiter ich springen muss.

14:01.640 --> 14:03.640
Und dafür sind solche Skalierungen ganz praktisch.

14:06.320 --> 14:11.720
Der Input vom Adresswerk kommt typischerweise aus dem Register-File,

14:12.540 --> 14:14.900
wo die Basis- und Indexregister drin sein könnten für

14:14.900 --> 14:15.740
Adressberechnungen.

14:16.180 --> 14:20.560
Oder es kann auch aus dem Datenbus-Buffer kommen, also quasi vom

14:20.560 --> 14:22.140
Hauptspeicher gelesen.

14:22.600 --> 14:26.640
Vom Hauptspeicher lesen könnte ich entweder ein Befehl und in dem

14:26.640 --> 14:28.660
Befehl kann ja ein Offset kodiert sein.

14:28.840 --> 14:31.200
Das könnte Input hier sein.

14:31.740 --> 14:35.380
Oder es könnte sein, dass ich eine mehrstufige Adressberechnung habe,

14:35.440 --> 14:38.920
wo ich quasi zwischendurch erst mal auf den Speicher zugreifen muss

14:38.920 --> 14:40.460
und dann darauf weiter zu rechnen.

14:40.700 --> 14:43.880
Dann wäre dieses zwischendurch auf den Speicher zugreifen, das käme

14:43.880 --> 14:47.480
hier raus und wird dann quasi für die nächste Adressberechnung, also

14:47.480 --> 14:49.500
immer noch für die gleiche, für den nächsten Schritt der gleichen

14:49.500 --> 14:52.220
Adressberechnung, hier wieder ins Adresswerk reingeladen.

14:53.320 --> 14:59.220
Andere Input könnte zum Beispiel sein eine bisher zwischendurch, also

14:59.220 --> 15:04.360
momentan zwischendurch ausgerechnete Adresse oder sogar, oder einfach

15:04.360 --> 15:08.060
der Programmzähler, also quasi der Befehlsteiger.

15:08.320 --> 15:12.000
Der Befehl, der als nächstes ausgeführt werden soll und der soll ja

15:12.000 --> 15:16.360
zum Beispiel, also der aktuelle Befehl wird genommen an den einen

15:16.360 --> 15:20.520
Input und es soll gesprungen werden um 7, das steht hier als Offset

15:20.520 --> 15:25.380
zum Beispiel in dem Befehl drin, um 7 mit 4 skaliert, das wäre dann

15:25.380 --> 15:28.840
quasi zusammenaddiert, wäre der neue Programmzähler.

15:28.920 --> 15:31.120
Das wäre so ein Beispiel Adressberechnung.

15:34.200 --> 15:38.080
Die Eingänge A und B, nochmal gucken, das waren hier der linke und der

15:38.080 --> 15:38.840
rechte Eingang.

15:40.380 --> 15:44.100
Wie gesagt Registersatz, Datenbuspuffer hatte ich, Programmzähler

15:44.100 --> 15:45.360
könnte der andere kommen.

15:45.360 --> 15:48.540
Ja gut, Adressbuspuffer, da kann natürlich auch aus dem

15:48.540 --> 15:54.180
Adressbuspuffer wieder zurückführen nach oben, um zum Beispiel eine

15:54.180 --> 15:57.280
verschachtelte Adressberechnung zu machen, wo quasi irgendwie mehrere

15:57.280 --> 15:59.640
Sachen addiert werden müssen, zum Beispiel ein Basis- und

15:59.640 --> 16:01.480
Indexregister.

16:01.800 --> 16:05.100
Wenn ich ja quasi erst irgendeine Startadresse plus Basis- plus

16:05.100 --> 16:07.780
Indexregister, muss ich ja mehrmals addieren, habe ja nur eine Ado

16:07.780 --> 16:10.280
hier drin, also muss ich irgendwie ein Zwischenergebnis haben, das

16:10.280 --> 16:13.140
Zwischenergebnis kann ich dann wieder, also Zwischenergebnis nach der

16:13.140 --> 16:16.520
Addition mit Basisadresse, kann ich wieder hier hochholen und kann

16:16.520 --> 16:19.800
dann hier die Indexadresse rein holen, dann noch plus Indexadresse,

16:19.920 --> 16:21.900
hatten wir ja verschiedene Adressberechnungen, wie wir das auch

16:21.900 --> 16:22.440
gemacht haben.

16:24.780 --> 16:28.700
Und den Ausgang kann ich wieder auch ausgeben, also ich kann den quasi

16:28.700 --> 16:32.780
auch hier oben, das was ich hier quasi als zum Beispiel Adresse

16:32.780 --> 16:35.020
ausgerechnet habe, kann ich entweder nehmen und direkt auf den

16:35.020 --> 16:39.580
Adressbus legen, zum Schraubenspeicher-Zugriff oder ich kann es über

16:39.580 --> 16:45.100
einen Hilfsregister, Speicherregister auch wieder zurück ins Register

16:45.100 --> 16:46.620
-File schreiben, würde ich auch hinkommen.

16:48.460 --> 16:52.080
Gut, aber das ist halt ein Beispiel für ein Adresswerk.

16:54.420 --> 16:57.440
Ja, wie schon gesagt, heute ist es typischerweise ein sediziertes

16:57.440 --> 16:57.940
Adresswerk.

16:58.200 --> 17:00.080
Früher war das häufig einfach die normale ALU.

17:00.680 --> 17:01.680
Warum ist es jetzt sediziert?

17:01.820 --> 17:03.760
Damit man Adressberechnungen parallel machen kann.

17:04.800 --> 17:07.400
Adressberechnungen werden vor allem dann lästig, lästig im Sinne von

17:07.400 --> 17:10.260
aufwendiger, wenn man virtuelle Speicherverwaltung hat, gucken wir

17:10.260 --> 17:10.980
später nochmal drüber.

17:15.240 --> 17:18.180
Ja, der nächste Punkt, der noch fehlt, ist die Systembus

17:18.180 --> 17:18.620
-Schnittstelle.

17:18.720 --> 17:20.160
Wir hatten einige der Sachen jetzt schon gesehen.

17:20.280 --> 17:22.360
Das hier zum Beispiel hatten wir gerade beim Adresswerk auch schon

17:22.360 --> 17:26.260
gesehen und die Systembus-Schnittstelle ist quasi alles, was irgendwie

17:26.260 --> 17:30.460
die CPU abkoppelt vom Systembus, also vom Datensteuer- und Adressbus.

17:31.060 --> 17:34.440
Und das sind halt im Wesentlichen hier erstmal die Daten- und

17:34.440 --> 17:35.660
Adressbus -Puffer.

17:36.080 --> 17:37.500
Und wo sind die angeschlossen?

17:38.260 --> 17:43.800
Ja, da ist zum Beispiel der Befehlszähler dran oder eine momentan

17:43.800 --> 17:46.900
temporäre oder final ausreichende Adresse von einer Adressberechnung.

17:47.320 --> 17:52.200
Und diese Abtrennung hier brauche ich einfach deswegen, weil ein

17:52.200 --> 17:56.300
Systembus, da könnten mehrere Komponenten angeschlossen sein, da

17:56.300 --> 17:59.400
könnten mehrere Komponenten gleichzeitig versuchen schreibend darauf

17:59.400 --> 18:02.460
zuzugreifen und das muss man irgendwie unterdrücken können.

18:03.020 --> 18:06.420
Also es darf halt immer nur einer schreibend auf zugreifen und da gibt

18:06.420 --> 18:10.520
es halt eben ein spezielles, kann man dazu Logik sagen, auf jeden Fall

18:10.520 --> 18:13.900
mal ein Gutter, das Three-State-Gutter, was quasi in der Lage ist,

18:14.040 --> 18:17.680
entweder eine Eingabe 1 zu 1 durchzulassen, so wie sie halt war, oder

18:17.680 --> 18:22.720
in der Lage ist, Schreibzugriffe zu unterbinden, also quasi sich quasi

18:22.720 --> 18:25.260
physikalisch vom Bus abzutrennen, indem die Verbindung an den Bus

18:25.260 --> 18:26.480
extrem hochohmig wird.

18:26.960 --> 18:31.480
Es müssen quasi alle Schreibanschlüsse an den Bus müssen abgetrennt

18:31.480 --> 18:35.120
werden, bis auf einen, der darf dann schreiben, also höchstens einer

18:35.120 --> 18:35.620
darf schreiben.

18:39.540 --> 18:42.460
Das waren die Systembusse hier unten, jetzt gibt es noch interne Busse

18:42.460 --> 18:46.300
und interne Busse sind quasi alles, was hier so als fette Linie gemalt

18:46.300 --> 18:46.560
ist.

18:47.280 --> 18:50.580
Das dient dazu, die Komponenten innerhalb der CPU, also Steuerwerk,

18:50.660 --> 18:53.860
Rechenwerk, Adressgenierung, Registerfall miteinander zu verbinden.

18:55.000 --> 18:58.700
Da fällt es jetzt schwer und macht man auch nicht, zwischen Daten- und

18:58.700 --> 19:02.460
Adressbus zu unterscheiden, einfach deswegen, weil je nachdem, was für

19:02.460 --> 19:05.460
eine Rechnung gerade durchgeführt wird, sind das halt eben mal

19:05.460 --> 19:09.400
Operanten von der eigentlichen Anwendung, die über die Busse geschickt

19:09.400 --> 19:13.760
werden oder es sind halt Adressen, die quasi auch gerade berechnet

19:13.760 --> 19:16.340
werden, als Vorbereitung auf den Hauptspeicherzugriff zum Beispiel,

19:16.460 --> 19:17.280
als Adressberechnung.

19:17.800 --> 19:21.080
Also es sind halt mal Daten, mal Operanten, die über die Busse gehen,

19:21.180 --> 19:22.400
deswegen wird da nicht unterschieden.

19:23.020 --> 19:26.080
Und wer quasi auf den Bus schreiben darf und wer vom Bus lesen darf,

19:26.140 --> 19:28.200
wird halt eben durch die Steuerung hier entschieden.

19:29.700 --> 19:31.740
Das orchestriert quasi den ganzen Ablauf.

19:35.170 --> 19:37.410
Es gibt noch mehr Einheiten logischerweise.

19:38.730 --> 19:42.130
Was es quasi bei allen CPUs gibt, ist die Memory-Management-Einheit,

19:42.190 --> 19:44.010
hatte ich auch schon ein paar mal erwähnt, virtuelle Speicher und

19:44.010 --> 19:44.310
sowas.

19:45.210 --> 19:49.570
Arithmetik -Einheit 10 im Sinne von Rechenwerk in groß, wo einfach

19:49.570 --> 19:52.630
mehrere Rechenoperationen parallel ausgeführt werden können.

19:53.130 --> 19:55.370
Cache-Speicher hatte ich auch schon mal erwähnt, gucken wir uns später

19:55.370 --> 19:56.770
irgendwann nochmal an.

19:59.090 --> 20:03.570
Und jetzt mal ein Beispiel, wie es in Wirklichkeit aussehen könnte.

20:03.710 --> 20:06.390
Könnte im Sinne von Pentium 4, ist es auch nicht mehr gerade so der

20:06.390 --> 20:08.010
modernste, also eigentlich überhaupt nicht.

20:08.250 --> 20:11.370
Aber ist egal, es ist immer noch vom Prinzip her sieht es bei quasi

20:11.370 --> 20:13.230
allen modernen Architekturen genauso aus.

20:13.850 --> 20:15.910
Was man auf jeden Fall sieht, ist, dass es ein großes buntes

20:15.910 --> 20:18.970
Durcheinander ist, aber versuchen wir mal die einzelnen Komponenten

20:18.970 --> 20:19.550
uns anzugucken.

20:19.610 --> 20:22.390
Wir fangen hier links oben an, das ist quasi die System-Bus

20:22.390 --> 20:24.510
-Stellstelle, also da geht es an den Hauptspeicher und sonstiges

20:24.510 --> 20:24.790
Zeugs.

20:25.210 --> 20:28.090
Da haben wir quasi eine Anbindung, hier steht quasi nur spezifiziert,

20:28.170 --> 20:31.970
wie schnell die Anbindung ist und danach gibt es quasi einen großen L2

20:31.970 --> 20:35.190
-Cache, also dieser schnelle Zwischenspeicher, damit der langsame

20:36.090 --> 20:37.970
Hauptspeicherzugriff vermieden werden kann im Idealfall.

20:38.510 --> 20:40.010
Und da gibt es zwei Pfade raus.

20:40.370 --> 20:43.290
Einmal geht es hier oben für die Befehle und hier unten für die Daten.

20:43.870 --> 20:46.150
Gucken wir uns hier oben erstmal die Befehle an.

20:48.510 --> 20:51.490
Da gibt es auch nochmal einen speziellen Befehl, ich sage mal Buffer

20:51.490 --> 20:52.250
jetzt einfach.

20:53.570 --> 20:57.390
Da gibt es auch irgendein Ding, was sich darum kümmert, bei

20:57.390 --> 21:01.290
Sprungbefehlen sich Gedanken darüber zu machen, wird der Sprung

21:01.290 --> 21:03.230
vermutlich genommen oder auch nicht.

21:03.410 --> 21:06.370
Also ich will nicht sagen rät, es spekuliert, hört sich freundlicher

21:06.370 --> 21:06.630
an.

21:07.150 --> 21:10.210
Es spekuliert, ob ein Sprung genommen wird oder nicht, um zu gucken,

21:10.270 --> 21:12.750
welche Befehle werden wohl als nächstes gebraucht, um die geeigneten

21:12.750 --> 21:14.150
Befehle schon mal anschaffen zu können.

21:14.610 --> 21:18.150
Und danach kommt erst nach diesem Befehls-Buffer der Befehls-Decoder,

21:18.710 --> 21:22.930
der eben dann, wir hatten es schon gesagt, x86 ist eine CISC-CPU, die

21:22.930 --> 21:26.090
innen drin gemicro-coded ist, die dann quasi an den Micro-Code

21:26.090 --> 21:30.630
-Speicher, einen ROM, einen EEPROM vielleicht, geht und da dann die

21:30.630 --> 21:34.610
richtige Befehls-Sequenz, Micro-Code- Befehls-Sequenz zur Ausführung

21:34.610 --> 21:36.030
anstößt.

21:36.870 --> 21:40.810
Da gibt es auch wieder einen Buffer für, der in diesem Fall die Micro

21:40.810 --> 21:44.790
-Ops zwischenspeichert und die zuletzt benötigten Befehls-Sequenzen

21:44.790 --> 21:49.710
einfach parat hat, wo in dem Fall eine relativ große Zahl oder 12.000

21:49.710 --> 21:53.510
Micro -Ops reinpassen würden, wieder mit irgendeinem Ding für die

21:53.510 --> 21:54.370
Schwungvorhersage.

21:54.510 --> 21:57.010
Schwungvorhersage kommt später, gucken wir uns das nochmal an, aber

21:57.010 --> 21:57.950
erstmal egal.

21:58.290 --> 22:01.970
Also hier sind jetzt quasi die Befehle, die jetzt schon dekodiert,

22:02.150 --> 22:06.550
also von CISC nach Micro-Op dekodiert, zur Ausführung bereitstehen.

22:07.210 --> 22:09.050
Na, nicht bereitstehen, anstehen, sagen wir mal.

22:10.230 --> 22:12.030
Und jetzt müssen wir noch die Ausführung machen.

22:12.510 --> 22:15.690
Hier gibt es jetzt so ein paar Einheiten, die lassen sich jetzt schwer

22:15.690 --> 22:17.970
erklären, zum Beispiel Rename Register.

22:18.230 --> 22:19.890
Warum sollte man Register umbenennen?

22:20.310 --> 22:22.290
Gibt es einen guten Grund für, um irgendwelche Abhängigkeiten

22:22.290 --> 22:23.070
aufzulösen?

22:23.570 --> 22:26.630
Bevor wir Abhängigkeiten hatten, lässt sich schwer erklären, wie man

22:26.630 --> 22:27.350
die auflösen kann.

22:27.630 --> 22:31.830
Also da wird irgendwie geplant, welche Ressourcen für die Berechnung

22:31.830 --> 22:34.950
von der Micro-Code, Micro-Op benutzt werden sollen.

22:36.170 --> 22:38.590
Ja, und hier kommen jetzt verschiedene Recheneinheiten.

22:38.890 --> 22:43.890
Einmal in bunt die Floating Point und hier in anders bunt die, was

22:43.890 --> 22:44.690
sind das für Farben?

22:45.270 --> 22:47.470
Egal, die Integer-Einheiten.

22:48.990 --> 22:50.670
Hier hätten wir jetzt quasi eine Unterscheidung.

22:50.750 --> 22:52.990
Es gibt hier also offenbar mehrere Hardware-Einheiten, ja.

22:53.350 --> 22:56.770
Und hier wird dann quasi entschieden, in welche von diesen Hardware

22:56.770 --> 22:58.190
-Einheiten soll der Befehl denn rein.

22:58.270 --> 23:00.730
Zum einen muss man gucken, was will der Befehl, muss halt in die

23:00.730 --> 23:02.050
passende Hardware-Einheit rein.

23:02.430 --> 23:04.890
Und dann kann man gucken, falls es mehrere verschiedene zur Auswahl

23:04.890 --> 23:06.530
gibt, welche von denen möchte man denn nehmen.

23:08.610 --> 23:12.030
Da gibt es das Register-File, was speziell für die Integer-Register da

23:12.030 --> 23:15.030
ist und halt eben ein Verbindungsnetzwerk, dass man, dass jede von den

23:15.030 --> 23:16.970
Einheiten auf jedes der Register zugreifen kann.

23:17.490 --> 23:19.130
Jetzt kann man schon sehen, hier gibt es eine ganze Menge

23:19.130 --> 23:19.930
Recheneinheiten.

23:20.310 --> 23:24.450
Hier gibt es einmal eine sogenannte langsame ALU, die heißt, positiver

23:24.450 --> 23:26.590
formuliert, macht die komplexe Befehle.

23:26.970 --> 23:30.050
Zum Beispiel sowas wie eine Division ist ein sehr aufwendiger Befehl.

23:30.110 --> 23:32.590
Der dauert halt länger als eine ODA-Verknüpfung zum Beispiel, ja, um

23:32.590 --> 23:33.770
mal extreme Beispiele zu machen.

23:34.290 --> 23:36.510
Dann gibt es noch normale ALUs.

23:37.830 --> 23:41.610
Hier ein zweier Block, also zwei ALUs, hier nochmal zwei ALUs für

23:41.610 --> 23:42.910
einfache Befehle.

23:43.010 --> 23:45.670
Da kann natürlich auch eine Addition dabei sein oder eine logische

23:45.670 --> 23:46.030
Operation.

23:48.170 --> 23:50.870
Hier hätten wir jetzt, hier oben konnte man es schon sehen, die sind

23:50.870 --> 23:52.110
quasi Speicherbefehle.

23:53.330 --> 23:55.790
Da gab es oben auch eine extra Queue für, wo Speicherbefehle

23:55.790 --> 23:59.670
eingereiht werden können und die Speicherbefehle gehen dann hier an

23:59.670 --> 24:04.130
zwei Blöcke, in denen jeweils zwei Adress-Generation-Units, Adress

24:04.130 --> 24:06.670
-Generierungseinheiten drin sind, die halt quasi die ganze

24:06.670 --> 24:08.070
Adressberechnung durchführen können.

24:08.490 --> 24:10.830
Und sobald die Adressberechnung fertig ist, man also weiß, auf welche

24:10.830 --> 24:13.670
Adresse man zugreifen möchte, kann man es dann hier unten an den

24:13.670 --> 24:14.690
Datenspeicher wählen.

24:15.090 --> 24:18.290
Wenn denn, ein Datenspeicher, man kann es sehen, ist hier

24:18.290 --> 24:20.570
unidirektional, der macht die Schreibzugriffe, der macht die

24:20.570 --> 24:24.630
Lesezugriffe und hier ist quasi der L1-Datenspeicher.

24:25.110 --> 24:28.910
Der ist also, L1 heißt der L1-Cache, der erste Cache-Level, L1, das

24:28.910 --> 24:33.970
hier war der zweite Cache-Level und der holt seine Daten dann wiederum

24:33.970 --> 24:37.230
aus dem L2 und wenn der sie nicht hat, dann muss aus dem Hauptspeicher

24:37.230 --> 24:37.890
geholt werden.

24:38.530 --> 24:41.110
Hier drüben noch die Floating-Point-Hardware-Einheiten.

24:41.690 --> 24:43.710
Also man kann sehen, es gibt eine ganze Menge Hardware-Einheiten und

24:43.710 --> 24:47.450
oben drüber so ein Orchestrierungsbereich, der quasi sich darum

24:47.450 --> 24:51.150
kümmert, wer wo Schlange stehen soll und warten soll, bis seine

24:51.150 --> 24:53.370
Operanten fertig sind oder bis er ausgeführt werden darf.

24:53.490 --> 24:57.650
Also ist schon etwas komplexer als die Sache, die wir gerade gesehen

24:57.650 --> 24:57.970
haben.

24:58.170 --> 25:00.730
Die ganzen System-Bussen und sowas, die wir gerade gesehen haben, sind

25:00.730 --> 25:01.750
hier alle nicht reingemalt.

25:01.830 --> 25:02.970
Das würde nicht so richtig gut hinpassen.

25:04.050 --> 25:10.250
Okay, gut, damit hätten wir das einfache Rechenmodell fertig.

25:10.910 --> 25:15.110
Das wäre jetzt schon, oder es ist eine funktionsfähige

25:15.110 --> 25:15.550
Rechenarchitektur.

25:15.650 --> 25:19.030
Damit kann man Programme ausführen, beliebige Programme, Turing

25:19.030 --> 25:20.310
-Complete -Sachen berechnen lassen.

25:21.030 --> 25:22.230
Ist ja schon mal nicht schlecht.

25:23.330 --> 25:25.530
Wie schon angekündigt, die erste Sache, wie man das Ganze ein bisschen

25:25.530 --> 25:29.290
schneller machen kann, wäre das nächste Kapitel, Kapitel 6, wo wir uns

25:29.290 --> 25:31.410
den wesentlichen Pipelining angucken.

25:31.970 --> 25:34.810
Also quasi Pipelining auf Deutsch.

25:36.430 --> 25:37.850
Wir werden ja gleich sehen, was es ist.

25:39.490 --> 25:46.710
Da gucken wir uns erstmal an, was ist es und dann, wie man es ungefähr

25:46.710 --> 25:48.450
aufteilen könnte, wie man die Pipeline stufen.

25:48.610 --> 25:49.210
Das sehen wir gleich.

25:49.890 --> 25:52.910
Und dann ein Beispiel von MIPS und DLX-Pipeline.

25:53.430 --> 25:57.290
Und dann, nachdem wir uns überlegt haben, wie es grob funktioniert und

25:57.290 --> 26:00.470
wie toll das eigentlich ist, guckt man es noch an, was da alles bei

26:00.470 --> 26:03.390
schief gehen kann und wie man das dann wieder repariert, damit es eben

26:03.390 --> 26:04.530
nicht passiert, dass es schief geht.

26:06.850 --> 26:10.750
Gut, wir hatten halt gerade gesehen in der MIMA-Architektur, dass da

26:10.750 --> 26:13.030
so eine relativ serielle Befehlsarbeitung passiert.

26:13.170 --> 26:15.330
Sprich, es kommt ein neuer Befehl raus und dann muss der über den

26:15.330 --> 26:18.110
Systembus mal in die eine Ecke, mal die andere Ecke, je nachdem, was

26:18.110 --> 26:19.210
der Befehl gerade tun muss.

26:19.950 --> 26:24.770
Und jetzt mal ganz grob gesagt, hätten wir jetzt quasi, wenn ich ein

26:24.770 --> 26:28.050
Befehl habe, dann muss der von mir aus vier verschiedene Sachen

26:28.050 --> 26:28.370
machen.

26:28.470 --> 26:30.630
Der muss aus dem Speicher geholt werden, der muss, was hatten wir

26:30.630 --> 26:33.370
noch, der muss im Register-File irgendwas machen, der muss in der ALU

26:33.370 --> 26:35.690
was berechnen und der muss dann vielleicht irgendwie auf den Speicher

26:35.690 --> 26:36.250
zugreifen.

26:36.590 --> 26:40.750
Also der muss mehrere einzelne Schritte machen, danach kommt der

26:40.750 --> 26:44.450
nächste Befehl, der macht mehrere einzelne Schritte und danach und so

26:44.450 --> 26:44.670
weiter.

26:45.630 --> 26:49.470
Und die Idee von Pipelining ist, kann man das nicht zeitlich versetzt

26:49.470 --> 26:51.210
gleichzeitig ausführen?

26:51.610 --> 26:55.870
Im Sinne von, geben wir den Kindern mal Namen, wenn der erste Schritt

26:55.870 --> 26:58.730
einfach derjenige ist, dass der Befehl aus dem Speicher, aus dem

26:58.730 --> 27:00.330
Programmspeicher rangeschafft wird.

27:01.290 --> 27:04.750
Sobald ich von dem ersten Befehl dieses Rangschaffen aus dem

27:04.750 --> 27:08.590
Hauptspeicher erledigt habe, ist ja die Einheit, die dafür zuständig

27:08.590 --> 27:15.110
ist, nämlich das Ding, was hier den Programmcounter plus 4 rechnet und

27:15.110 --> 27:18.370
danach den Adressbuspuffer mit der Adresse füttert und dann auf den

27:18.370 --> 27:21.250
Befehl wartet, der vom Speicher, vom Hauptprogrammspeicher wieder

27:21.250 --> 27:21.770
zurückkommt.

27:22.490 --> 27:25.930
Nachdem das für den ersten Befehl erledigt ist, dann ist diese Einheit

27:25.930 --> 27:29.370
ja frei, die ist ja verfügbar, die macht ja gerade nichts anderes.

27:29.950 --> 27:32.650
Könnte die mich schon mal den nächsten Befehl ranschaffen?

27:32.850 --> 27:35.570
Das wäre quasi hier, die macht das gleiche nochmal für den nächsten

27:35.570 --> 27:40.330
Befehl, während der vorherige Befehl, das kommt danach vielleicht, in

27:40.330 --> 27:43.770
der Dekodierphase steckt, der dann rausfindet, was soll dieser Befehl

27:43.770 --> 27:44.390
eigentlich tun.

27:45.270 --> 27:48.950
Und wenn der fertig dekodiert wurde, dann könnte der Befehl, der hier

27:48.950 --> 27:52.190
schon geholt worden ist, ja auch in die Dekodierphase rein, während

27:52.190 --> 27:56.390
der hier oben in die Ausführung geht, also das Rechenwerk, die ALU

27:56.390 --> 27:57.770
benutzt, um irgendwas zu berechnen.

27:58.010 --> 27:59.670
Okay, dann können wir auch gleichzeitig den nächsten Befehl

27:59.670 --> 28:00.150
ranschaffen.

28:00.630 --> 28:04.150
Also einfach von der Idee her könnte das funktionieren.

28:04.690 --> 28:07.470
Wenn wir uns das hier anschauen, da haben wir uns mit diesem Systembus

28:07.470 --> 28:10.970
hier durch ein riesiges Bottleneck, ein Flaschenhals eingebaut, wo ja

28:10.970 --> 28:13.710
quasi alle Kommunikation über diesen Systembus geht.

28:13.830 --> 28:15.350
Also ist das klar, das können wir so nicht lassen.

28:15.750 --> 28:19.350
Da müssen wir irgendwie mehrere Kommunikationsnetzwerke aufziehen,

28:19.630 --> 28:23.850
damit das gleichzeitig passieren kann, dass ein neuer Befehl hier in

28:23.850 --> 28:27.210
das Steuerwerk reingelangt, während ein ganz anderer Befehl, der

28:27.210 --> 28:31.170
vorher schon geholt worden ist, Daten aus dem Registerfile in die ALU

28:31.170 --> 28:31.490
bringt.

28:31.890 --> 28:34.670
Das wäre quasi hier, dass der eine Befehl, der möchte vielleicht

28:34.670 --> 28:36.890
gerade rechnen, also zum Beispiel Registerfile, was in die ALU

28:36.890 --> 28:41.510
reinbringen, während der hier gerade, der oder der, wahrscheinlich der

28:41.510 --> 28:47.070
hier aus dem ROM, aus dem Befehlsspeicher ins Dekodiereinheit den

28:47.070 --> 28:47.890
Befehl bringen möchte.

28:48.030 --> 28:49.310
Das muss ja irgendwie parallel passieren können.

28:50.490 --> 28:53.550
Okay, also das ist die Idee vom Pipelining.

28:54.090 --> 28:57.810
Und wahrscheinlich der erste zumindest mal bekannte Fall war

28:57.810 --> 29:03.110
tatsächlich das Fließband, so heißt es auf Deutsch, Fließband von

29:03.110 --> 29:07.570
Henry Ford, was er in seinem Werk, in wo auch immer der gesessen hat,

29:08.150 --> 29:12.930
eingeführt hat, um die Produktion von Autos zu beschleunigen.

29:13.070 --> 29:16.650
Es war im Wesentlichen quasi so, dass früher hat man quasi einen

29:16.650 --> 29:20.750
Arbeitsplatz gehabt und hat da so Stück für Stück das Auto aufgebaut.

29:21.450 --> 29:24.910
Da hat auch quasi dann ein Arbeiter alle Schritte gemacht, bis das

29:24.910 --> 29:26.110
Auto halt eben fertig war.

29:26.150 --> 29:28.430
Und dann wurde das Auto rausgekarrt und hat da quasi neue Teile

29:28.430 --> 29:32.110
angeliefert bekommen, hat an seinem Arbeitsplatz das nächste Auto

29:32.110 --> 29:32.490
gebaut.

29:33.590 --> 29:39.310
Und Pipelining ist halt eben, ja mehrere, mehrere Vorteile eigentlich.

29:39.710 --> 29:44.390
Zum einen muss nicht mehr ein Arbeiter alle Schritte beherrschen, das

29:44.390 --> 29:46.610
ist nämlich gar nicht so einfach ein Auto zu bauen, also versucht es

29:46.610 --> 29:51.330
halt mal, sondern der Arbeiter muss nur noch den einen Schritt können,

29:51.610 --> 29:54.410
der an seiner Pipeline-Stufe halt gerade zu tun ist.

29:54.750 --> 29:57.390
Das könnte ein sehr einfacher Schritt sein, von mir aus die Tür

29:57.390 --> 29:58.090
einhängen.

29:58.210 --> 30:00.870
Ich schätze, das ist kein Schritt, den man alleine macht, aber

30:00.870 --> 30:02.310
irgendein Beispiel.

30:02.550 --> 30:05.030
Also keine Ahnung, Reifen montieren, irgendwas.

30:06.230 --> 30:08.930
Und er muss nur das können und dafür hat er auch die richtigen

30:08.930 --> 30:09.570
Werkzeuge.

30:09.730 --> 30:12.570
Also für Reifen montieren hat er dann quasi die Druckluft, Knarren,

30:12.650 --> 30:14.610
was auch immer, um die Muttern festzuziehen.

30:14.930 --> 30:17.550
Und er hat natürlich die Reifen da liegen, aber er hat nicht mehr die

30:17.550 --> 30:20.210
ganzen anderen Werkzeuge rumliegen, die man für die ganzen anderen

30:20.210 --> 30:20.890
Arbeiten macht.

30:21.030 --> 30:23.490
Also es ist aufgeräumter, er hat nur die Sachen da, die er braucht.

30:23.810 --> 30:27.030
Also der Arbeiter ist optimiert, der Arbeiter und seine Umgebung,

30:27.110 --> 30:30.770
seine Arbeitsumgebung ist optimiert auf die Aufgabe, die er tut.

30:31.230 --> 30:36.150
Und dann ist das Auto, was quasi vorbeigeschoben wird, also nicht von

30:36.150 --> 30:38.930
Hand, sondern von einem Fließband halt eben.

30:39.110 --> 30:42.750
Das Auto kommt angerollt, dann muss er seine Operation tun, das Auto

30:42.750 --> 30:45.370
rollt weiter zur nächsten Station, wo dann die nächste Operation, wo

30:45.370 --> 30:46.250
es tut, getan wird.

30:46.250 --> 30:48.810
Währenddessen kommt das nächste Auto an, der montiert wieder Reifen.

30:49.310 --> 30:51.150
Wahrscheinlich ist Reifen montiert wirklich kein gutes Beispiel, aber

30:51.150 --> 30:53.210
was auch immer die so tun an Autos.

30:56.650 --> 30:59.570
Ja, aus irgendwelchen Gründen kommt hier nicht Autos, sondern Wäsche

30:59.570 --> 31:00.210
als Beispiel.

31:00.950 --> 31:05.350
Also man kann in der Tat, wenn man wirklich sehr, sehr viel Wäsche zu

31:05.350 --> 31:09.410
waschen hat, so wirklich viel, dann lohnt es sich vielleicht auch, das

31:09.410 --> 31:12.290
in mehrere Stufen einzuteilen und parallel abzuarbeiten.

31:12.290 --> 31:17.190
Man könnte das zum Beispiel in die Stufen einteilen, Waschmaschine

31:17.190 --> 31:21.670
befüllen, von der Waschmaschine in den Trockner reinmachen.

31:22.670 --> 31:27.950
Nach dem Trockner muss es, ich schätze man sollte es, also ich halte

31:27.950 --> 31:30.330
nichts von Bügeln, aber wenn, dann sollte man es wahrscheinlich vor

31:30.330 --> 31:32.030
dem Falten machen, nicht nach dem Falten.

31:32.330 --> 31:34.550
Aber wie auch immer, also da könnte noch so ein anderer Schritt

31:34.550 --> 31:37.270
dazwischen sein und am Ende muss man es irgendwie in den Schrank

31:37.270 --> 31:38.270
einpacken.

31:39.750 --> 31:43.110
Und wenn man das jetzt quasi auf so einer Zeitachse sich angucken

31:43.110 --> 31:50.290
würde, hätte man offenbar vier verschiedene ABCD Waschladungen, die zu

31:50.290 --> 31:50.930
tun sind.

31:51.570 --> 31:54.550
Und wenn man das jetzt quasi sequentiell nacheinander arbeitet, fällt

31:54.550 --> 31:58.290
auf, die Waschmaschine ist hier, nachdem sie hier fertig gearbeitet

31:58.290 --> 32:01.210
hat, ist die leer, die wartet nur.

32:01.750 --> 32:03.590
Und erst hier wird sie wieder benutzt.

32:04.070 --> 32:08.670
Ich glaube, die Zeiten sind symbolisch gesehen.

32:08.970 --> 32:10.990
Halbe Stunde Waschmaschine, bei mir dauert das irgendwie länger, ich

32:10.990 --> 32:11.510
weiß auch nicht.

32:12.710 --> 32:15.810
Jetzt kann man das Ganze offensichtlich parallelisieren, sobald man

32:15.810 --> 32:19.530
die erste Waschmaschinenladung in den Trockner gestopft hat, ist die

32:19.530 --> 32:20.530
Waschmaschine ja frei.

32:20.670 --> 32:25.510
Jetzt kann man von der nächsten Wäscheladung die schonmal in die

32:25.510 --> 32:26.530
Waschmaschine reinmachen.

32:27.030 --> 32:32.650
Und das sind dann quasi die gepipelteinten Ausführungen von vier

32:32.650 --> 32:37.630
verschiedenen Eingabedaten, also schmutzigen Wäschekörben, also Körben

32:37.630 --> 32:40.530
mit schmutziger Wäsche, die hier abgearbeitet werden.

32:40.630 --> 32:43.270
Und die Idee ist klar, es geht viel schneller, als wenn man das

32:43.270 --> 32:43.910
nacheinander macht.

32:43.970 --> 32:44.530
Das ist die Idee.

32:44.690 --> 32:47.090
Es geht schneller, weil man hier Parallelität ausnutzt.

32:47.490 --> 32:50.630
Es ist aber auch klar, dass irgendjemand ziemlich hektisch in der

32:50.630 --> 32:53.270
Gegend rumspringen muss, um Sachen von A nach B zu karren.

32:53.410 --> 32:54.750
Ich kann das nicht mehr alleine machen.

32:55.050 --> 32:57.310
Ich müsste ja gleichzeitig hier jemanden haben, der es in den Schrank

32:57.310 --> 33:00.030
legt und faltet, bügelt, trocknet und wäscht.

33:01.170 --> 33:03.450
Also das geht von alleine, das machen die Maschinen, aber das in den

33:03.450 --> 33:05.750
Schrank legen und das Falten, Bügeln, das muss ein Mensch machen.

33:05.850 --> 33:07.570
Also werden auf jeden Fall mehrere Personen benötigt.

33:07.670 --> 33:11.030
Da Parallelität kostet auch Ressourcen, die es ausführen können.

33:11.510 --> 33:18.890
Bei der CPU hat man häufig eine Aufteilung in folgende fünf Stufen.

33:19.410 --> 33:23.250
Also es gibt CPUs, die haben wirklich nur eine dreistufige Pipeline.

33:23.990 --> 33:29.130
Fünf ist eine relativ häufige Aufteilung für normale, kleine, einfache

33:29.130 --> 33:30.650
RISC -CPUs.

33:30.690 --> 33:32.010
Fünf bis sieben, sagen wir mal.

33:32.730 --> 33:38.590
Und wenn ich mir in Pentium 4, glaube ich, hatte 21 Pipeline-Stufen,

33:38.970 --> 33:40.810
das ist dann schon wieder zu viel.

33:41.010 --> 33:43.510
Also zu viele, um es jetzt irgendwie so sinnvoll nachvollziehen zu

33:43.510 --> 33:43.770
können.

33:44.810 --> 33:46.630
Aber das gucken wir uns auch ein Beispiel noch später an.

33:46.710 --> 33:49.970
Aber diese fünf Stufen, wie sie hier stehen, sind typische Stufen.

33:50.090 --> 33:52.190
Deswegen werden wir die auch im Folgenden benutzen.

33:52.250 --> 33:53.750
Und die kommen auch bei der MIPS-CPU vor.

33:53.890 --> 33:55.450
Wir werden im nächsten Beispiel ein MIPS-CPU machen.

33:55.550 --> 33:57.150
Das sind auch genau diese fünf Stufen.

33:58.130 --> 34:02.510
Eine Stufe ist quasi, dass man den Befehl erst mal herholen muss aus

34:02.510 --> 34:03.570
dem Programmspeicher.

34:04.130 --> 34:07.470
Dann muss man herausfinden, was will der Befehl eigentlich von einem.

34:07.910 --> 34:10.590
Also man muss sich überlegen, welche Einheiten werden dafür benutzt,

34:10.650 --> 34:13.670
welche Steuersignale müssen irgendwie vorbereitet werden.

34:14.470 --> 34:18.150
Danach, nachdem klar ist, was der Befehl will, weiß man auch, auf

34:18.150 --> 34:19.550
welchen Daten er das tun möchte.

34:20.050 --> 34:23.090
Also muss man die Operanden dafür ranschaffen aus dem Register-File

34:23.090 --> 34:24.890
oder aus der E-Media zum Beispiel.

34:26.490 --> 34:28.070
Das ist die eigentliche Rechnung.

34:28.270 --> 34:29.550
Das, was man eigentlich machen wollte.

34:29.670 --> 34:32.250
Das ist der Teil, der wirklich produktiv Nutzen erbringt.

34:32.350 --> 34:33.210
Der rechnet irgendwie.

34:33.810 --> 34:36.550
Und wenn der fertig ist mit Rechnen, muss man das Ergebnis noch

34:36.550 --> 34:39.650
irgendwo hinbringen, zum Beispiel wieder ins Register-File zurück.

34:40.150 --> 34:43.510
Das wären so die typischen Pipeline-Stufen von einer RISC-CPU.

34:46.010 --> 34:49.910
Und hier nochmal, das ist dasselbe wie vorhin als Beispiel

34:49.910 --> 34:53.250
aufgezeichnet, sequentiell gegen die gepipelte Ausführung.

34:56.590 --> 34:59.910
Auf einer Zeitachse betrachtet, sieht man jetzt schon, dass dann,

35:00.050 --> 35:04.710
nachdem die Pipeline einmal gefüllt ist, würde quasi dieser Zustand

35:04.710 --> 35:09.610
herrschen, dass in jeder Stufe ein Befehl ist.

35:09.690 --> 35:12.790
Also jetzt wäre quasi hier ein Befehl, der ist in der ersten Stufe,

35:12.910 --> 35:16.570
ein anderer Befehl, also ein anderer, in der zweiten Stufe, in der

35:16.570 --> 35:17.930
dritten, vierten und fünften Stufe.

35:18.270 --> 35:21.190
Alle fünf Stufen sind beschäftigt, alle fünf Stufen arbeiten.

35:21.670 --> 35:25.490
Dieser Zustand hier in der Mitte, der bleibt dann für eine Weile an,

35:28.230 --> 35:33.010
der dauert an, bis irgendwann keine neuen Befehle mehr herangeschafft

35:33.010 --> 35:34.590
werden, also Programm zu Ende oder wie auch immer.

35:35.090 --> 35:38.830
Dann leert sich die Pipeline wieder, bis irgendwann alles fertig ist.

35:38.910 --> 35:42.950
Also am Anfang füllt sich die Pipeline mit Befehlen, am Ende leert sie

35:42.950 --> 35:45.310
sich wieder, wenn quasi keine neuen Befehle mehr herankommen.

35:45.610 --> 35:47.910
Könnte auch andere Gründe geben, warum keine neuen Befehle

35:47.910 --> 35:51.610
herankommen, zum Beispiel weil der Speicher lange braucht, um den

35:51.610 --> 35:53.330
nächsten Befehl zu holen.

35:53.390 --> 35:55.790
Dann würde auch so eine Lücke in der Pipeline-Stufe entstehen, wo

35:55.790 --> 35:58.890
quasi hintere Stufen rechnen weiter, vorne kommt nichts Neues rein,

35:58.930 --> 36:00.190
hat man so eine Lücke innen drin.

36:03.110 --> 36:05.390
Was man hier auch schon gut erkennen kann, das kommt auch gleich

36:05.390 --> 36:11.110
nochmal, ich habe jetzt ja, damit das effizient funktioniert, habe ich

36:11.110 --> 36:15.530
hier, nein nicht effizient, damit es überhaupt funktioniert, habe ich

36:15.530 --> 36:17.450
hier irgendeine Art von Taktung vorgegeben.

36:17.850 --> 36:21.970
Das heißt, alle Stufen haben hier gleich viel Zeit.

36:22.550 --> 36:25.810
Wenn eine der Stufen deutlich schneller ist als die anderen, dann

36:25.810 --> 36:26.730
bringt mir das nichts.

36:27.330 --> 36:29.650
Weil es muss dann ja trotzdem, die muss ja dann trotzdem warten auf

36:29.650 --> 36:30.350
die anderen Stufen.

36:30.650 --> 36:35.670
Also die langsamste der Stufen, die ist die Bremse und wie viel

36:35.670 --> 36:36.950
schneller die anderen sind.

36:37.030 --> 36:38.990
Ich meine, wenn es eine langsamste gibt, sind die anderen schneller,

36:39.110 --> 36:40.970
eventuell minimal, eventuell sehr viel schneller.

36:41.450 --> 36:43.410
Wie viel sie schneller sind, ist egal.

36:43.410 --> 36:47.710
Im Idealfall sind alle Schritte gleich aufwendig, im Sinne von

36:47.710 --> 36:48.990
benötigen gleich viel Zeit.

36:49.130 --> 36:51.230
Dann ist die Pipeline immer, dann ist die Hardware immer gut

36:51.230 --> 36:53.950
beschäftigt, dann sind alle Pipeline-Stufen die ganze Zeit über

36:53.950 --> 36:54.510
beschäftigt.

36:57.350 --> 36:58.390
Ein paar Wörter.

36:59.710 --> 37:04.790
Pipelining, also das Machen von Pipelining bedeutet, dass man eine

37:04.790 --> 37:10.630
Maschinenoperation, ein Assemblerbefehl zerlegt in mehrere Phasen und

37:10.630 --> 37:13.870
die werden dann eine Phase nach der anderen ausgeführt.

37:14.470 --> 37:18.110
Und Takt-Synchron heißt einfach, dass diese Striche hier nicht

37:18.110 --> 37:21.130
irgendwelche beliebigen Striche variabler Länge sind, sondern das ist

37:21.130 --> 37:23.950
einfach genau dann, wo der Takt sagt, jetzt hättest du fertig sein

37:23.950 --> 37:27.390
sollen oder umgekehrt, wo der Takt erst dann kommen darf, wenn

37:27.390 --> 37:28.510
wirklich alle fertig sind.

37:28.570 --> 37:30.810
Man muss sich vorher überlegen, wie lange braucht der längste und das

37:30.810 --> 37:32.230
ist dann die Taktdauer sozusagen.

37:33.030 --> 37:35.190
Das ist mit dem Takt-Synchron hier gemeint.

37:37.010 --> 37:42.730
Die ganzen Verarbeitungseinheiten, die Phasen, die hier zerlegt

37:42.730 --> 37:45.850
wurden, also die Phasen, in die die Maschinenoperation zerlegt wurde,

37:46.370 --> 37:50.010
die ganzen Einheiten davon zusammen, ist dann die Pipeline.

37:50.150 --> 37:53.050
Also die Pipeline besteht aus der Hardware, die all diese Stufen, die

37:53.050 --> 37:56.490
wir jetzt haben, fünf hatten wir gerade, ausführen können, plus die

37:56.490 --> 37:58.910
Verbindung zwischen den Stufen, plus die Steuerung davon.

38:00.330 --> 38:03.550
Das ist ein relativ allgemeines Wort, also es könnte auch eine

38:03.550 --> 38:06.410
Automobil -Pipeline sein und hier haben wir halt normalerweise eine

38:06.410 --> 38:09.370
Befehls -Pipeline, wo halt eben die Assemblerbefehle, die

38:09.370 --> 38:12.650
Maschinenbefehle, aufgeteilt werden, in diese Pipeline-Stufen.

38:17.840 --> 38:20.220
Was wir jetzt hier sehen, ist quasi die Aufteilung.

38:20.300 --> 38:26.420
Wir hätten jetzt hier eine Stufe, das ist quasi ein reines Schaltnetz,

38:26.540 --> 38:30.460
also hier ist innen drin kein Zustand, kein Register, einfach nur

38:30.460 --> 38:34.580
Logik gerade hintereinander und hier hätten wir dann Register und hier

38:34.580 --> 38:36.980
jetzt wieder die nächste Stufe, die letzte Stufe.

38:37.660 --> 38:40.780
Die Idee ist ja wirklich, dass hier die Berechnung gemacht wird, die

38:40.780 --> 38:42.680
in dieser Stufe halt eben gemacht wird, das heißt die

38:42.680 --> 38:45.360
Adressberechnung, Befehle holen, muss ja auch irgendwie mal ein

38:45.360 --> 38:49.380
bisschen was rechnen und alle Kommunikationen von einer Stufe in die

38:49.380 --> 38:53.140
nächste, wird durch Register von der nächsten Stufe abgekoppelt.

38:53.520 --> 38:55.860
Hier innen drin sind dann keine weiteren Register mehr, also eine

38:55.860 --> 39:00.660
saubere Trennung zwischen reiner Logik und reiner Zustandsübergabe an

39:00.660 --> 39:04.420
die nächste Stufe und all diese Register hängen am gleichen

39:04.420 --> 39:06.840
Taktsignal, das mit dem Synchronen halt eben gemeint.

39:08.620 --> 39:11.580
Jetzt haben wir hier eine Verzögerungszeit, also hier habe ich einen

39:11.580 --> 39:15.680
Logik -Gatter, das hat irgendwelche Eingänge, die aus den... hier ist

39:15.680 --> 39:19.180
nur ein Bit, das sind mehrere Bits, die hier herauskommen, die aus der

39:19.180 --> 39:23.020
vorherigen Stufe oder wo auch immer die erste Stufe ist, rauskommen,

39:23.080 --> 39:25.940
hier reinkommen, also wir haben hier Eingaben, Primary Inputs,

39:25.940 --> 39:29.800
irgendwas wird berechnet und wir haben Primary Outputs, die quasi

39:29.800 --> 39:32.220
ausgegeben werden in das nächste Register.

39:32.740 --> 39:35.160
Und hier dazwischen dauert es eine Zeit, da gibt es quasi einen Pfad,

39:35.200 --> 39:40.160
der dauert am längsten und der bestimmt für Stufe 1 wäre dann dieses

39:40.160 --> 39:46.200
TAUI, das bestimmt die Zeit um die erste Stufe, bis die auf jeden Fall

39:46.200 --> 39:48.080
fertig ist, bis der langsamste Pfad fertig ist.

39:49.320 --> 39:53.500
Die Register brauchen auch irgendeine Zeit, das wäre das hier und die

39:53.500 --> 39:56.620
Frage ist, wie schnell kann das Ganze laufen, was die Taktfrequenz,

39:56.760 --> 40:00.440
wie lang, wie kurz ist der Taktzyklus, das ist das folgende, die

40:00.440 --> 40:04.700
langsamste Stufe, also quasi die langsamste von all diesen Stufen

40:04.700 --> 40:08.700
hier, plus die Zeit, die das Register noch braucht, also die halt eben

40:08.700 --> 40:11.860
dann quasi vor der Taktflanke, die Daten wirklich da sein müssen,

40:11.940 --> 40:13.260
damit der Register sie übernehmen kann.

40:13.660 --> 40:14.940
Das wäre die Taktfrequenz.

40:16.480 --> 40:20.600
Nicht richtig, das wäre die Taktdauer in Sekunden, Taktzyklus.

40:20.960 --> 40:22.500
Die Frequenz wäre halt eben eins durch davon.

40:23.800 --> 40:27.480
Also wenn zum Beispiel der hier 10 Nanosekunden braucht und der

40:27.480 --> 40:31.000
braucht 3 Nanosekunden und der braucht 4 Nanosekunden, dann ist halt

40:31.000 --> 40:36.340
eben der Taktzyklus diese 10 Nanosekunden plus was auch immer dieses

40:36.340 --> 40:39.400
Register noch braucht, nicht so viel eigentlich, also wäre das halt

40:39.400 --> 40:43.480
eben insgesamt eine 10 Komma bisschen was Nanosekundenzyklusdauer und

40:43.480 --> 40:46.800
10 Nanosekunden sind gerade 100 Megahertz, das wäre dann quasi die

40:46.800 --> 40:48.040
Geschwindigkeit von der Pipeline.

40:48.420 --> 40:53.040
Und dass die hier eigentlich schon nach drei, was ist egal, die müssen

40:53.040 --> 40:53.420
dann warten.

40:58.330 --> 40:59.970
Noch ein paar Wortdefinitionen.

41:00.030 --> 41:01.630
Wir haben jetzt gerade gesehen, die Pipeline-Stufen.

41:03.630 --> 41:06.450
Manchmal, sehr, sehr selten, nie gehört.

41:06.950 --> 41:08.270
Auch Pipeline-Segmente genannt.

41:09.610 --> 41:14.050
Dazwischen sind getaktete Register und in Pipeline-Takt, Pipeline

41:14.050 --> 41:17.170
-Maschinentakt ist halt eben dieses Maximum, was wir gerade gesehen

41:17.170 --> 41:17.350
haben.

41:17.430 --> 41:19.290
Die Zeit, die es braucht, um von einer Stufe zur nächsten

41:19.290 --> 41:19.990
weiterzugeben.

41:20.150 --> 41:22.630
Die Zeit, die die langsamste Stufe braucht, um fertig zu rechnen.

41:25.590 --> 41:29.670
Dabei ist die Idee normalerweise oder die ideale Idee ist wie folgt,

41:29.890 --> 41:33.110
wenn ich eine k-stufige Pipeline habe, also ich habe k-Stufen in

41:33.110 --> 41:40.530
meiner Pipeline, dann sollte ein Befehl, diese k-Stufen, im Jahrfall

41:40.530 --> 41:42.950
auch in k-Takten durchlaufen.

41:43.250 --> 41:47.070
Also, dass ein Befehl immer genau ein Takt in einer Stufe bleibt.

41:47.590 --> 41:50.270
Das nehmen wir jetzt einfach mal im Folgenden an.

41:50.870 --> 41:52.250
Es ist aber nicht immer so.

41:52.590 --> 41:55.570
Man kann sich ganz schnell ein Beispiel überlegen, zum Beispiel eine

41:55.570 --> 41:57.990
von den Stufen hier wird ja wohl die Rechenstufe sein, die

41:57.990 --> 42:02.370
Ausführungsstufe und da hatten wir uns schon überlegt, dass vielleicht

42:02.370 --> 42:06.070
die oder-Verknüpfung von zwei Registern doch ein bisschen schneller

42:06.070 --> 42:10.430
gehen sollte, als ich sag mal eine Division von zwei Zahlen und sei es

42:10.430 --> 42:12.650
nur integer, ignorieren wir mal Floating Point, das wäre noch

42:12.650 --> 42:15.770
schlimmer, sei es nur eine integer Division, die braucht echt länger.

42:16.150 --> 42:19.450
Das heißt, wenn man diese Ausführungsstufe jetzt so langsam machen

42:19.450 --> 42:23.650
würde, dass die integer Division fertig wird in einem Takt, dann sind

42:23.650 --> 42:24.910
alle Stufen sehr langsam.

42:25.030 --> 42:26.870
Dann habe ich eine sehr, sehr langsame Frequenz.

42:27.370 --> 42:30.650
Könnte ich nicht stattdessen lieber sowas machen, dass ich sage, es

42:30.650 --> 42:34.310
gibt Multi-Cycle Befehle, also ein Befehl, der dann auch vielleicht

42:34.310 --> 42:38.830
mal drei oder vier oder zehn Takte in der Ausführungsstufe festhängen

42:38.830 --> 42:39.030
kann.

42:39.110 --> 42:41.530
Der sagen kann, ich bin noch nicht fertig in der Ausführungsstufe, ich

42:41.530 --> 42:44.950
brauche noch mehr Takte und dann müssen die Befehle davor warten.

42:45.990 --> 42:50.290
Einfach um dieses Ungleichgewicht zwischen sehr schnellen Logik

42:50.290 --> 42:53.290
-Operationen und langsameren Integer-Divisionen oder sogar Floating

42:53.290 --> 42:55.070
Point -Operationen so ein bisschen auszugleichen.

42:55.650 --> 42:56.870
Macht man auch so.

42:57.170 --> 43:00.750
Aber der Einfachheit halber, ignorieren wir sowas einfach mal.

43:01.110 --> 43:03.710
Also hier ist jetzt idealerweise, wo hatten wir es?

43:06.010 --> 43:13.450
Idealerweise würde eine k-stufige Pipeline k-Takte pro Befehl

43:13.450 --> 43:16.370
brauchen, bis der Befehl hinten wieder rausgekommen ist.

43:18.830 --> 43:21.230
Ja, mit diesem Idealen haben wir es auch so, dass quasi zu jeder

43:21.230 --> 43:26.370
beliebigen Zeit, die Pipeline ist voll, ich habe immer k-verschiedene

43:26.370 --> 43:29.330
Befehle in den verschiedenen Pipeline-Stufen.

43:29.470 --> 43:32.070
Wenn ich k-Pipeline-Stufen habe, da sind auch k-Befehle, die sich

43:32.070 --> 43:36.710
quasi gleichzeitig in der Ausführung befinden, in Flight, sagt man da

43:36.710 --> 43:37.210
manchmal auch.

43:39.370 --> 43:45.150
Außerdem brauchen Befehle, um die Pipeline zu verlassen, also zu

43:45.150 --> 43:46.710
durchqueren, k-Takte.

43:46.830 --> 43:49.370
Also er muss vorne rein, muss durch alle Stufen durch und pulstert

43:49.370 --> 43:50.330
danach hinten wieder raus.

43:52.810 --> 43:56.050
Das wäre quasi, was ich gerade gesagt habe, die Latenz.

43:56.450 --> 43:59.810
Latenz ist immer von irgendwas beginnt bis genau dasselbe ist fertig

43:59.810 --> 44:02.230
und beginnt ist hier die Ausführung von einem Maschinenbefehl.

44:02.590 --> 44:06.130
Ist fertig heißt, er hat seine letzte Stufe abgearbeitet und das

44:06.130 --> 44:10.490
dauert halt eben, wenn ich pro Stufe einen Takt brauche, dann braucht

44:10.490 --> 44:11.410
das halt eben k-Takte.

44:13.210 --> 44:17.010
Also im Idealfall, in der idealen Vorstellung hätte ich quasi, dass

44:17.010 --> 44:22.070
ich für egal welchen Befehl genau k-Takte brauche, um den Befehl durch

44:22.070 --> 44:23.490
die k-Stufen durchzuschleusen.

44:24.190 --> 44:27.270
Und wenn das so wäre, habe ich auch k-Befehle, die gleichzeitig in

44:27.270 --> 44:28.310
verschiedenen Stufen drin sind.

44:29.750 --> 44:33.790
Dann habe ich hier noch was, den Durchsatz.

44:34.630 --> 44:36.790
Und Durchsatz ist jetzt erstmal irgendwie so ein Maß für

44:36.790 --> 44:37.610
Geschwindigkeit.

44:37.750 --> 44:42.510
Wie viele Befehle pro Zeit im Allgemeinen, jetzt hier erstmal pro Takt

44:42.510 --> 44:46.050
im Spezielleren, werden eigentlich fertiggestellt.

44:46.270 --> 44:50.130
Mit dieser idealen Annahme kommt quasi in jedem Takt hinten ein Befehl

44:50.130 --> 44:51.390
raus, fertig.

44:51.970 --> 44:54.230
Und wenn halt eben mal eine Stufe länger braucht oder wenn vielleicht

44:54.230 --> 44:56.130
ein Speicherzugriff dazwischen ist, der Speicherzugriff braucht

44:56.130 --> 45:01.190
länger, dann sind es halt weniger als ein Befehl pro Takt.

45:01.410 --> 45:03.430
Das muss man mitteln, im Schnitt dann halt eben.

45:03.970 --> 45:07.590
Aber im Idealen, von der idealen Vorstellung ist es genau ein Befehl

45:07.590 --> 45:08.830
pro Takt, der fertig wird.

45:10.970 --> 45:13.710
So, jetzt versuchen wir das Ganze mal zu vergleichen.

45:14.430 --> 45:16.310
Pipeline gegen sequenzielle Ausführung.

45:16.390 --> 45:18.490
Wir hatten ja vorhin schon gesehen, es sollte schneller werden.

45:19.170 --> 45:21.910
Wie viel wird es denn schneller?

45:22.110 --> 45:26.750
Beispiel, wir wollen N Befehle ausführen, große Zahl, mit einer

45:26.750 --> 45:29.570
Pipeline, die K-Stufen hat.

45:30.650 --> 45:34.710
Wenn wir es erstmal ohne Pipeline ausführen, also wir haben immer noch

45:34.710 --> 45:38.370
die gleichen K-Ausführungseinheiten, die gleichen K

45:38.370 --> 45:40.430
-Ausführungseinheiten, der Befehl muss geholt werden, der muss

45:40.430 --> 45:42.410
dekodiert werden, das verschwindet ja nicht, das muss ich ja trotzdem

45:42.410 --> 45:42.710
machen.

45:43.010 --> 45:46.790
Aber jetzt mache ich das Ganze ohne Pipelining, also ich spare ein

45:46.790 --> 45:49.290
bisschen Hardware, ich kann mit einem internen Bus alle

45:49.290 --> 45:53.530
Kommunikationen machen, wie wir es bei Abima gesehen haben, dann

45:53.530 --> 45:54.410
brauche ich wie lange?

45:54.850 --> 46:00.290
Ich habe N Befehle, jeder Befehl muss einzählen, die K-Takte, die K

46:00.290 --> 46:05.490
-Berechnungsstufen durchlaufen, jede Berechnungsstufe braucht einen

46:05.490 --> 46:09.650
Takt, brauche ich N mal K-Takte, weil jeder Befehl seine K

46:09.650 --> 46:12.590
-Berechnungsstufen für sich selbst machen muss und keine parallele

46:12.590 --> 46:14.070
Ausführung zulässt.

46:15.470 --> 46:22.650
Mit Pipelining werden es so viel, N plus K minus 1, eine komische

46:22.650 --> 46:23.370
Zahl, warum?

46:28.090 --> 46:34.230
Ich brauche N-Taktzyklen, um die N Befehle, die ich ja ausführen

46:34.230 --> 46:40.070
wollte, um die N Befehle in die erste Pipelinstufe zuzuführen,

46:40.070 --> 46:44.070
reinzustopfen und dann wird ja quasi, die werden ja weitergegeben,

46:44.190 --> 46:46.990
also ich kann ja unter dieser idealen Annahme, dass der Befehl nur

46:46.990 --> 46:50.450
einen Takt pro Stufe braucht, kann ich ja quasi in jedem Takt neuen

46:50.450 --> 46:55.850
Befehl vornherein zuführen und dann rechnet die halt durch.

46:56.290 --> 47:00.990
Nach N-Takten habe ich den letzten Befehl in die erste Stufe getan,

47:02.330 --> 47:05.330
jetzt muss ich aber noch warten, bis der letzte Befehl fertig ist.

47:05.330 --> 47:08.630
In der ersten Stufe ist er ja schon, da habe ich ihn ja schon

47:08.630 --> 47:12.870
reingemacht, jetzt fehlen noch K minus 1 zusätzliche Pipeline-Stufen,

47:13.030 --> 47:17.070
die kommen noch und da muss ich noch K minus 1 Takte warten, danach

47:17.070 --> 47:20.650
ist der letzte Befehl aus der letzten Pipeline-Stufe wieder draußen,

47:21.170 --> 47:23.310
Pipeline ist leer, alle Befehle sind fertig.

47:23.990 --> 47:26.470
So setzt sich dieses N plus K minus 1 zusammen.

47:26.830 --> 47:30.630
N Befehle, um die N Befehle mal zur Ausführung anzustoßen und der

47:30.630 --> 47:34.550
letzte Befehl muss dann noch K minus 1 Takte warten, bis er wirklich

47:34.550 --> 47:35.630
fertig gerechnet ist.

47:39.290 --> 47:41.550
Und wenn man das hat, dann kann man den Speedup ausrechnen.

47:41.650 --> 47:45.650
Speedup ist immer ein Maß für eine Geschwindigkeitssteigerung.

47:45.770 --> 47:48.970
Also es gibt irgendeine Basis, mit der man vergleichen möchte.

47:49.430 --> 47:52.530
Diese Basis hat einen Speedup von 1x, also das wäre quasi genauso

47:52.530 --> 47:55.430
schnell, 2x wäre dann ich bin doppelt so schnell und so weiter.

47:56.510 --> 48:02.290
Und das rechnet man aus, indem man halt eben Basis geteilt durch das

48:02.290 --> 48:02.690
Neue.

48:03.270 --> 48:07.750
Das Neue braucht im Idealfall hoffentlich hier auf jeden Fall weniger

48:07.750 --> 48:08.250
Takte.

48:08.710 --> 48:11.370
Also hat man hier eine kleinere Zahl stehen, also holt hier eine Zahl

48:11.370 --> 48:15.550
größer 1 raus und wieviel, naja das ist dann halt eben der Speedup.

48:15.870 --> 48:19.650
Und wieviel es halt eben ist, hängt davon ab, wieviel Befehle man

48:19.650 --> 48:22.290
hatte und wieviel Pipeline-Stufen man hat.

48:24.450 --> 48:26.550
Gucken wir uns davon mal ein paar Beispiele an.

48:27.990 --> 48:33.710
Hier ist aufgetragen, wieviel Befehle sollen ausgeführt werden.

48:34.590 --> 48:37.450
Die verschiedenen Linien sind hier, kann man sehen, sind 4K gleich 2,

48:37.710 --> 48:40.910
10, also für Anzahl Pipeline-Stufen, für 2 Pipeline-Stufen, 10

48:40.910 --> 48:45.130
Pipeline -Stufen, 25 ist ein bisschen viel, aber nur so als Beispiel.

48:46.090 --> 48:50.690
50 und 100 Pipeline-Stufen, also keine CDU der Welt hat 100 Pipeline

48:50.690 --> 48:50.970
-Stufen.

48:51.090 --> 48:53.850
Aber nur mal zum Gucken, wie sich diese Formel so entwickeln würde.

48:54.430 --> 48:58.790
Und dann ist hier aufgetragen, wie ist jetzt der Speedup für so und so

48:58.790 --> 49:00.770
viele Befehle und für Pipeline-Stufen.

49:01.430 --> 49:05.870
Was man auf jeden Fall sieht ist, das Ganze flacht aus, wenn N groß

49:05.870 --> 49:10.630
wird, das ist jetzt keine große Überraschung, und es wird desto

49:10.630 --> 49:12.950
schneller, desto mehr Pipeline-Stufen ich habe.

49:16.750 --> 49:19.130
Zum Beispiel, man kann es jetzt nicht wirklich gut sehen, aber hier

49:19.130 --> 49:22.430
bei diesem K gleich 10 Pipeline-Stufen befinden wir uns auf jeden Fall

49:22.430 --> 49:24.410
mal irgendwo in der Nähe von der 10.

49:24.890 --> 49:28.250
Also nicht ganz erreicht, aber ziemlich deutlich in der Nähe davon.

49:28.830 --> 49:32.390
Wenn man sich das halt überlegen möchte, für diesen Speedup, wenn man

49:32.390 --> 49:38.290
Anzahl Befehle gegen Unendlich gehen lässt, dann dieses ziemlich große

49:38.290 --> 49:39.590
Unendlich hier würde verschwinden.

49:39.690 --> 49:42.790
Das hier ist ja dann Unendlich, das hier ist Unendlich plus eine sehr,

49:42.930 --> 49:43.590
sehr kleine Zahl.

49:44.030 --> 49:47.790
Also das ist dann weg, also hier ist eine 1, das ist eine 1, bleibt K

49:47.790 --> 49:51.490
durch 1 über, also das geht gegen K, gegen Anzahl Pipeline-Stufen.

49:52.830 --> 49:56.630
So gesehen, dieses für Pipeline-Stufen, für 100 Pipeline-Stufen, das

49:56.630 --> 49:57.790
steigt ja auch noch.

49:57.890 --> 50:00.670
Wir haben hier oben die 90 als obere Grenze, wenn man dann auch

50:00.670 --> 50:03.810
deutlich mehr Befehle machen würde, würde man auch irgendwann in die

50:03.810 --> 50:05.790
Nähe von der 100 rankommen.

50:07.050 --> 50:11.370
Also gute Sache, der Speedup wird desto höher, desto mehr Pipeline

50:11.370 --> 50:12.010
-Stufen ich habe.

50:12.230 --> 50:15.210
Das steht hier im Wesentlichen, was eine gute Sache ist.

50:17.590 --> 50:23.950
Ein Durchsatz ist jetzt die Frage, wie viele Befehle pro Zeit, pro

50:23.950 --> 50:26.750
Sekunde werden eigentlich fertiggestellt.

50:27.850 --> 50:33.410
Wenn ich Befehle pro Sekunde haben möchte, dann rechne ich sowas aus

50:33.410 --> 50:37.590
wie, wie viele Befehle pro Takt schaffe ich, also auf dem abstrakten

50:37.590 --> 50:42.490
Konstrukt Takt, abstrakte Zeit, und dann umgekehrt, ja wie viele Takte

50:42.490 --> 50:43.930
pro Sekunde kriege ich denn hin.

50:44.110 --> 50:45.870
Also das wäre jetzt quasi Megaherz.

50:46.250 --> 50:51.290
Megaherz ist ja, wie viele Takt-Clock-Schwingungen pro Sekunde kriege

50:51.290 --> 50:51.650
ich hin.

50:55.670 --> 50:57.030
Das würde so aussehen.

50:59.530 --> 51:08.530
Angenommen, ich will n Befehle ausführen, dann habe ich schon gesehen,

51:08.910 --> 51:15.050
ich brauche dafür so viele Takte, also n Takte, um alle Befehle in die

51:15.050 --> 51:17.990
Pipeline reinzuschieben und dann noch ein paar Takte mehr, um die

51:17.990 --> 51:19.830
letzten Befehle aus der Pipeline wieder rauszubekommen.

51:22.610 --> 51:25.770
Das habe ich jetzt eben nicht für ein Befehl, sondern für n Befehle

51:25.770 --> 51:26.110
gemacht.

51:26.970 --> 51:31.950
Also das hier wäre genau der Teil Befehle pro Takt.

51:33.150 --> 51:38.630
Nochmal, das hier ist genau der Teil hier.

51:41.620 --> 51:44.440
N Befehle brauche ich so viele Takte für.

51:44.800 --> 51:48.660
Und der andere Teil ist, wie viele Takte pro Zeit kriege ich hin.

51:49.360 --> 51:53.400
Dieses Tau, hatten wir vorhin gesehen, bei der Berechnung, wie schnell

51:53.400 --> 51:57.280
ist die Taktfrequenz, wie lang ist die Zyklusdauer, also die längste

51:57.280 --> 51:58.740
Pipeline -Stufe plus ein bisschen Register.

52:00.260 --> 52:05.280
Das ist quasi die Zeit in Sekunden, also Nanosekunden, die so eine

52:05.280 --> 52:06.500
Pipeline -Stufe dann braucht.

52:07.600 --> 52:13.760
Und eins geteilt durch die Zeit in Nanosekunden ist halt gerade, wie

52:13.760 --> 52:16.100
viele Takte pro Zeit kriege ich fertig.

52:16.520 --> 52:18.920
Also nur dieses Tauber, wie viele Nanosekunden, also wie viele

52:18.920 --> 52:23.340
Sekunden pro Takt, eins durch, wie viele Takte pro Sekunde kriege ich

52:23.340 --> 52:23.500
hin.

52:24.540 --> 52:26.700
Das ist genau der Teil hier.

52:27.720 --> 52:29.220
Und das zusammen ist dann der Durchsatz.

52:29.460 --> 52:33.400
Befehle pro Sekunde aufgeschlüsselt als Befehle pro Takt und Takte pro

52:33.400 --> 52:33.760
Sekunde.

52:38.170 --> 52:42.850
Da kann man sich auch wieder überlegen, wie schnell wird das, wenn ich

52:42.850 --> 52:47.870
ganz viele Befehle, also beliebig viele Befehle ausführen lasse.

52:48.510 --> 52:52.110
Gleiche Überlegung wie vorhin, dieses n und n plus kleine Zahl, das

52:52.110 --> 52:55.270
nimmt sich gerade nichts, das verschwindet und dieses 1 durch Tau

52:55.270 --> 52:55.890
bleibt über.

52:57.930 --> 53:03.950
Und das ist dann einfach die Grenze meines Durchsatzes.

53:04.570 --> 53:05.570
Was steht jetzt hier?

53:06.410 --> 53:11.910
Hier steht im Wesentlichen, je höher die Frequenz, desto höher der

53:11.910 --> 53:12.490
Durchsatz.

53:13.290 --> 53:16.010
Die Taktzyklusdauer oder 1 durch Taktzyklusdauer, das ist die

53:16.010 --> 53:16.510
Frequenz.

53:17.230 --> 53:21.030
Also die Grenze für Anzahl der Befehle ist gerade so schnell, wie die

53:21.030 --> 53:22.370
Frequenz meiner CPU ist.

53:22.710 --> 53:25.810
Alles unter dieser idealisierten Annahme, dass jeder Befehl genau

53:25.810 --> 53:27.450
einen Takt pro Stufe braucht und so weiter.

53:27.570 --> 53:31.230
Aber unter dieser idealisierten Annahme, je Frequenz, desto schneller,

53:31.730 --> 53:33.810
je Anzahl Pipeline-Stufen, desto schneller.

53:35.930 --> 53:39.050
Auch wieder mal am Beispiel einfach anguckt, wie es sich entwickeln

53:39.050 --> 53:39.350
würde.

53:39.830 --> 53:43.190
Hier Anzahl der Befehle, also die Endlichkeit liegt irgendwo so hier

53:43.190 --> 53:43.610
in der Ecke.

53:44.930 --> 53:51.650
Der Durchsatz, also Befehle pro ja was auch immer das jetzt für eine

53:51.650 --> 53:52.630
Zeiteinheit sein soll.

53:53.010 --> 53:54.430
10 hoch 7 irgendwas.

53:55.110 --> 53:55.810
Keine Ahnung.

53:56.390 --> 53:59.330
Zumindest kann man sehen, die Tendenz ist das gleiche wie vorhin.

53:59.450 --> 54:01.970
Das flacht aus nach hinten hin.

54:03.290 --> 54:04.850
Oh, wir haben auch eine 100 Nanosekunden.

54:05.050 --> 54:07.030
Das ist eine sehr, sehr, sehr langsame CPU-Frequenz.

54:08.050 --> 54:14.130
Das flacht aus nach hinten hin und wir haben hier für 10 und 50

54:14.130 --> 54:16.010
Stufen.

54:16.690 --> 54:21.450
Also nochmal, das obere mit dem höheren Durchsatz ist der mit den 10

54:21.450 --> 54:26.510
Stufen und der untere ist der mit dem mehr Stufen.

54:26.730 --> 54:28.250
Der hat aber einen geringeren Durchsatz.

54:28.950 --> 54:30.770
Also vorhin der Speedup hatten wir gesehen.

54:31.010 --> 54:32.330
Je höher, desto Speedup.

54:32.990 --> 54:35.690
Und hier haben wir jetzt aber gesehen, bei gleicher Frequenz 100

54:35.690 --> 54:37.530
Nanosekunden relativ langsam, aber ist ja egal.

54:38.010 --> 54:42.330
Bei gleicher Frequenz ist jetzt die höhere Anzahl Pipeline-Stufen

54:42.330 --> 54:42.910
schlechter.

54:43.110 --> 54:43.710
Woran liegt das?

54:43.990 --> 54:47.610
Naja, mit 500 Befehlen sind wir noch sehr weit von der Endlichkeit

54:47.610 --> 54:48.090
entfernt.

54:48.190 --> 54:49.650
Also die würden sich schon noch wieder annähern.

54:49.810 --> 54:52.850
Ja, aber wenn ich halt wenig Befehle habe, wobei wenig durchaus

54:52.850 --> 54:56.850
zigtausend sein können, dann macht die Anzahl der Stufen, das ist ja

54:56.850 --> 55:00.870
gerade der Unterschied dieses plus K minus 1, das macht es ja schon

55:00.870 --> 55:01.610
bemerkbar.

55:01.830 --> 55:05.770
Weil bei wenig Befehlen habe ich immer noch das K minus 1, was ich als

55:05.770 --> 55:07.610
Extra -Takte brauche, um den letzten Befehl hinten wieder

55:07.610 --> 55:08.150
rauszukommen.

55:08.250 --> 55:09.550
Das dauert halt einfach ein paar Takte.

55:10.050 --> 55:12.790
Und je weniger Befehle ich habe, desto relevanter sind die paar Takte,

55:12.790 --> 55:13.370
die es dauert.

55:14.130 --> 55:17.630
Hier oben habe ich die gleichen Vergleiche mit einer 50 Nanosekunden

55:17.630 --> 55:21.450
-CPU -Frequenz oder Zyklusdauer, 50 Nanosekunden-Zyklusdauer, wieder

55:21.450 --> 55:23.290
mit 10 und 50 Pipeline-Stufen.

55:23.790 --> 55:25.310
Hier ist der Unterschied sogar etwas größer geworden.

55:29.880 --> 55:32.940
Gut, also das war jetzt Pipelining, wie es im Allgemeinen

55:32.940 --> 55:33.460
funktioniert.

55:33.460 --> 55:37.080
Was die Idee darin ist, wie man Metriken wie Speedup und Durchsatz

55:37.080 --> 55:38.060
ausrechnen kann.

55:38.620 --> 55:41.580
Und jetzt wollen wir uns mal überlegen, wie baue ich jetzt eine CPU,

55:41.880 --> 55:43.580
die dieses Pipelining auch wirklich kann.

55:43.720 --> 55:44.660
Was sind die Schritte?

55:44.780 --> 55:46.800
Haben wir schon gesehen, das sind diese fünf Schritte, die wir vorhin

55:46.800 --> 55:47.500
schon gesehen haben.

55:48.020 --> 55:49.680
Wie baue ich jetzt das Ganze zusammen?

55:49.900 --> 55:53.960
Am Beispiel von der MIPS-CPU oder wie es hier steht, mit der MIPS

55:53.960 --> 55:55.060
-Befehlsarchitektur erstmal.

55:56.720 --> 55:59.720
Wir hatten bei MIPS, hatten wir schon gesehen, diese drei typischen

55:59.720 --> 56:01.180
verschiedenen Befehlsformate.

56:01.320 --> 56:04.340
Wir werden uns jetzt quasi ein Befehlsformat nach dem anderen,

56:04.480 --> 56:08.640
eigentlich nur die oberen beiden, anschauen, was dafür eigentlich, was

56:08.640 --> 56:13.680
getan werden soll, was dafür erforderlich ist, was das für uns

56:13.680 --> 56:16.740
bedeutet, was wir für Rechneinheiten haben müssen und wie wir die

56:16.740 --> 56:19.460
ganzen zusammenstöpseln müssen, damit am Ende auch wirklich die

56:19.460 --> 56:20.760
richtige Berechnung dabei herauskommt.

56:23.440 --> 56:25.240
Also, welche Komponenten brauche ich?

56:25.540 --> 56:28.340
Egal, welcher Befehl, wirklich egal.

56:28.720 --> 56:30.660
Ich brauche auf jeden Fall erstmal einen Befehlsspeicher.

56:30.940 --> 56:33.040
Vorher weiß ich ja gar nicht, welchen Befehl ich ausführen soll.

56:33.600 --> 56:36.460
Der Befehlsspeicher wird irgendwo im System vorhanden sein müssen.

56:36.700 --> 56:39.780
Der hat eine Adresse als Input, der hat ein Befehl, also 200-Bit

56:39.780 --> 56:41.520
-Befehl von mir aus, als Output.

56:42.360 --> 56:45.520
Ich werde auf jeden Fall auch haben müssen einen Befehlszeiger, der

56:45.520 --> 56:47.120
mir sagt, welcher Befehl ist dran.

56:47.540 --> 56:52.180
Da kann man schon ahnen, das wird wohl irgendwie, der Befehlszeiger

56:52.180 --> 56:56.760
wird wohl irgendwie die Adresse vom Befehlsspeicher werden.

56:57.840 --> 57:01.160
Und ich brauche auf jeden Fall auch zur Berechnung des nächsten

57:01.160 --> 57:04.560
Befehls einen Addierer, der dann quasi zum Beispiel sagt, plus 1 oder

57:04.560 --> 57:05.120
plus 4.

57:05.660 --> 57:09.220
Der sagt, der nächste Befehl ist 4-Byte weiter, da solltest du

57:09.220 --> 57:12.620
nächstes Mal den Befehl herholen.

57:13.360 --> 57:14.980
Okay, das brauche ich auf jeden Fall.

57:16.120 --> 57:17.680
Wie stöpse ich das Ganze zusammen?

57:18.240 --> 57:25.220
Ich nehme den Befehlszeiger, der gibt mir die Adresse, auf die ich den

57:25.220 --> 57:28.680
Befehlsspeicher zugreifen soll, da putzelt der Befehl raus, den ich

57:28.680 --> 57:29.800
jetzt ausführen soll.

57:32.080 --> 57:36.040
Und dann habe ich noch, ich nehme auch die Adresse, die im

57:36.040 --> 57:41.480
Befehlszeiger gerade drin stand, und addiere eine 4, das wäre quasi 4

57:41.480 --> 57:45.720
-Byte später, ist der nächste Befehl und das speichere ich ab, wieder

57:45.720 --> 57:50.360
als Input im Programme-Counter, im Takt danach, also das Befehl, das

57:50.360 --> 57:54.120
hier ist ein Register, was auch taktbasiert ist, im nächsten Takt wird

57:54.120 --> 57:58.000
es diesen Plus-4-Wert übernehmen und dann den Plus-4-Wert an die

57:58.000 --> 57:58.940
Adresse hier anlegen.

57:59.600 --> 58:03.580
Das ist dann im Wesentlichen, wobei im Wesentlichen heißt, da fehlt

58:03.580 --> 58:08.820
noch was, aber im Wesentlichen die Pipeline-Stufe für das Holen des

58:08.820 --> 58:09.580
nächsten Befehls.

58:09.940 --> 58:12.720
Ich habe irgendwie eine Rechnung und ich habe einen Speicherzugriff.

58:16.500 --> 58:19.200
Die nächsten Sachen hängen jetzt davon ab, was der Befehl machen will.

58:19.320 --> 58:22.540
Jetzt kann ich in der Dekodierphase gucken, was will der Befehl von

58:22.540 --> 58:25.880
mir, was will der da tun und da hatten wir zum Beispiel die R-Typ

58:25.880 --> 58:29.280
-Befehle gesehen, die folgendes Befehlsformat haben, wo ein paar Bits

58:29.280 --> 58:34.480
sagen, das ist ein R-Typ-Befehl, drei Register werden angegeben, es

58:34.480 --> 58:37.680
wird auf jeden Fall ein Shift-Amount angegeben, der hat eine

58:37.680 --> 58:40.460
Bedeutung, falls es ein Shift-Befehl ist, sonst nicht und eine ALU

58:40.460 --> 58:42.180
-Funktion wird angegeben.

58:42.400 --> 58:47.020
Beispiele, arithmetisch-logische Operationen, Vergleichsbefehle.

58:47.340 --> 58:49.840
Wichtig ist, ich habe drei Operanten und die Operanten stehen im

58:49.840 --> 58:50.120
Register.

58:50.640 --> 58:55.220
Also habe ich jetzt eigentlich zwei Lesezugriffe auf das

58:55.220 --> 58:58.680
Operantenregister, um jetzt die Daten ran zu schaffen für diesen

58:58.680 --> 59:02.460
Befehl und irgendwann später, wenn die Berechnung abgeschlossen ist,

59:02.540 --> 59:05.940
also das ist echt später dann, irgendwann später werde ich noch einen

59:05.940 --> 59:10.060
Schreibzugriff haben auf das Register, was hier als Zielregister

59:10.060 --> 59:10.900
angegeben wurde.

59:11.640 --> 59:15.580
Das heißt insbesondere auch, ich muss mir die ganze Zeit merken, was

59:15.580 --> 59:16.520
das Zielregister war.

59:16.520 --> 59:21.320
Also in der Dekodierphase hole ich mir die Operantenregister raus, die

59:21.320 --> 59:24.140
kann ich gleich dem Registerfeil geben und das Zielregister muss ich

59:24.140 --> 59:26.180
mir merken während der Ausführungsphase.

59:26.360 --> 59:27.980
Ich muss es mir merken, auch wenn ich es nicht brauche in der

59:27.980 --> 59:29.100
Ausführung, aber ich muss es mir merken.

59:29.460 --> 59:32.580
Und irgendwann der letzte der fünf Pfeilplanstufen war ja Ergebnis

59:32.580 --> 59:33.400
zurückschreiben.

59:33.660 --> 59:36.560
Das ist genau dieses Schreiben aufs Zielregister und dann muss ich

59:36.560 --> 59:40.380
wissen, was wollte denn dieser Befehl, wohin dieser Befehl das

59:40.380 --> 59:41.320
Ergebnis geschrieben haben.

59:41.400 --> 59:42.160
Dann muss ich es wissen.

59:44.780 --> 59:45.060
Gut.

59:45.680 --> 59:51.020
Ich habe also ein Registerfeil, hier ist also ein Sammelsurium von

59:51.020 --> 59:53.600
mehreren Registern mitgemeint, nicht nur eins, Registerfeil.

59:56.040 --> 01:00:01.280
Ich habe aus dem Befehlswort, habe ich drei Register, die kodiert

01:00:01.280 --> 01:00:04.520
sind, jeweils mit fünf Bits, also reicht es genau um 32 verschiedene

01:00:04.520 --> 01:00:05.360
Register zu adressieren.

01:00:05.480 --> 01:00:07.880
Also habe ich hier höchstwahrscheinlich ein 32 Einträge großes

01:00:07.880 --> 01:00:14.000
Registerfeil und kann diese fünf verschiedenen Adressen hier anlegen.

01:00:17.680 --> 01:00:19.920
Das Beispiel, was hier jetzt kommt, ist erstmal ohne Pipelining

01:00:19.920 --> 01:00:22.280
gedacht, deswegen können wir auch hier direkt die Schreibadresse

01:00:22.280 --> 01:00:22.720
anlegen.

01:00:23.920 --> 01:00:26.320
Also wir haben quasi die beiden Lese- und die Schreibadresse, alle

01:00:26.320 --> 01:00:29.400
drei Adressen werden hier angelegt und als Ergebnis kommen halt eben

01:00:29.400 --> 01:00:34.020
von den Lesezugriffen kommen die Daten, die Leseports, die Daten der

01:00:34.020 --> 01:00:37.280
beiden angefragten Register raus und gleichzeitig brauche ich aber

01:00:37.280 --> 01:00:40.640
noch, okay, erstmal die ALU, ich brauche eine ALU, die dann die

01:00:40.640 --> 01:00:43.600
Berechnung durchführt, wahrscheinlich auf den Registerdaten kann man

01:00:43.600 --> 01:00:47.640
ahnen und die ALU braucht eine Steuerung und die Steuerung wird dann

01:00:47.640 --> 01:00:50.980
irgendwie, das kommt jetzt noch ein bisschen später, die Steuerung,

01:00:51.140 --> 01:00:54.360
welcher Befehl gemacht wird, wird irgendwie vom Opcode abhängen und

01:00:54.360 --> 01:00:56.200
von diesem Function-Wert abhängen.

01:00:58.060 --> 01:01:02.320
Dann habe ich hier noch ein Steuersignal soll, also einfach nur ja

01:01:02.320 --> 01:01:06.160
oder nein, soll ein Register geschrieben werden, wenn ja ist es dieses

01:01:06.160 --> 01:01:10.780
Register mit der Adresse so und so und wenn es geschrieben wird, dann

01:01:10.780 --> 01:01:13.680
ist auch relevant, was für Daten geschrieben werden sollen, also muss

01:01:13.680 --> 01:01:15.040
ich auch irgendwie einen Dateneingang haben.

01:01:15.200 --> 01:01:18.500
Also hier kommen quasi für die beiden Lesen die Daten raus und für das

01:01:18.500 --> 01:01:20.900
Schreiben, wenn geschrieben werden soll, die Daten rein.

01:01:23.960 --> 01:01:27.380
Das muss ich jetzt irgendwie zusammenbasteln, es ist jetzt nicht

01:01:27.380 --> 01:01:29.260
zufällig so gemalt, dass es gerade gut passt.

01:01:30.340 --> 01:01:36.060
Aus dem Befehl direkt sehe ich, welche Registeradressen gelesen oder

01:01:36.060 --> 01:01:37.300
geschrieben werden sollen.

01:01:37.960 --> 01:01:43.080
Die beiden gelesenen werden für die normalen R-Typ-Befehle direkt an

01:01:43.080 --> 01:01:49.860
die ALU angeschlossen und das Ergebnis davon kann quasi direkt an den

01:01:49.860 --> 01:01:51.740
Schreibeingang angeschlossen werden.

01:01:52.220 --> 01:01:54.780
Das wäre jetzt noch ohne Pipelining, so einfach nur, so wird das ganze

01:01:54.780 --> 01:01:55.960
logisch zusammengeknüpft.

01:01:55.960 --> 01:01:58.700
Ich kann das ganze ohne Pipelining betreiben, einfach in einem sehr

01:01:58.700 --> 01:02:02.400
langen, dauernden Takt wäre kein Problem, könnte ich die Berechnung so

01:02:02.400 --> 01:02:03.020
durchführen lassen.

01:02:07.190 --> 01:02:13.910
Der nächste Befehlstyp wären zum Beispiel Ladespeicherbefehle, dafür

01:02:13.910 --> 01:02:18.090
brauche ich den I-Befehlstyp, brauche ich auch noch für andere Sachen,

01:02:18.230 --> 01:02:21.610
aber zum Beispiel laden, speichern, da habe ich halt eben nur zwei

01:02:21.610 --> 01:02:25.490
Register kodiert, ein Opcode, das habe ich immer, bei allen habe ich

01:02:25.490 --> 01:02:29.790
ein 6-Bit-Opcode vorne dran, hier jetzt nur zwei Register kodiert und

01:02:29.790 --> 01:02:31.770
noch ein 16-Bit-Immediate.

01:02:32.210 --> 01:02:35.430
Im Rahmen von Ladespeicherbefehlen ist das so zu verstehen, dass quasi

01:02:35.430 --> 01:02:40.390
einer der Register, einer der beiden Register, keine Ahnung welcher,

01:02:40.870 --> 01:02:46.710
als Basisregister plus Offset und der Offset ergibt sich aus einer

01:02:46.710 --> 01:02:49.650
Signed Extension von dem 16-Bit-Immediate, die sollen also zusammen

01:02:49.650 --> 01:02:52.930
modiert werden und das war dann quasi die Adressberechnung und auf

01:02:52.930 --> 01:02:57.370
diese Adresse wird dann quasi ladend oder schreibend zugegriffen.

01:02:57.510 --> 01:03:00.910
Und dann das jeweils andere der beiden Register sagt dann an, was in

01:03:00.910 --> 01:03:02.970
den Speicher geschrieben werden soll oder wenn es noch im Speicher

01:03:02.970 --> 01:03:06.290
geladen wird, wo das Geladene ins Register freigeschrieben werden

01:03:06.290 --> 01:03:06.530
soll.

01:03:09.710 --> 01:03:13.390
Dafür brauche ich jetzt zusätzliche Komponenten und zwar brauche ich

01:03:13.390 --> 01:03:16.250
einmal ganz zwingend eine Vorzeichenerweiterung, also ich habe ja

01:03:16.250 --> 01:03:21.230
dieses 16-Bit-Immediate-Wert, das muss ich jetzt auf eine 32-Bit-Zahl

01:03:21.230 --> 01:03:24.170
erweitern und das muss ich halt eben machen, nicht indem ich Nullen

01:03:24.170 --> 01:03:26.550
vorn dran klatsche, das könnte ja eine negative Zahl sein, also muss

01:03:26.550 --> 01:03:28.030
ich gucken, dass das Vorzeichen erhalten bleibt.

01:03:28.470 --> 01:03:34.910
Darum die Vorzeichenerweiterung, Eingabe 16-Bit, Ausgabe 32-Bit und um

01:03:34.910 --> 01:03:38.250
auf Hauptspeicher zuzugreifen, brauche ich einen Hauptspeicher, den

01:03:38.250 --> 01:03:39.870
ich dann irgendwo anschließen muss im System.

01:03:40.430 --> 01:03:43.450
Der hat einfach eine Adresse, auf die zugegriffen werden soll.

01:03:44.190 --> 01:03:46.930
Er hat ein Steuersignal, das sagt, es soll geschrieben werden.

01:03:47.110 --> 01:03:49.670
Im Fall, dass geschrieben werden soll, habe ich hier einen Eingabebus,

01:03:49.730 --> 01:03:52.630
der sagt, was geschrieben werden soll, also ein 32-Bit-Wert, der sagt,

01:03:52.730 --> 01:03:55.630
was geschrieben werden soll an dieser Adresse und ich habe ein

01:03:55.630 --> 01:03:57.670
Steuersignal, das heißt, es soll nicht geschrieben, sondern gelesen

01:03:57.670 --> 01:03:59.670
werden, also die beiden bitte nicht gleichzeitig eins.

01:04:00.410 --> 01:04:03.830
Es soll gelesen werden und im Fall, dass gelesen wird, wird halt eben

01:04:03.830 --> 01:04:07.570
dann das Gelesene an diesem Port ausgegeben.

01:04:07.690 --> 01:04:10.010
Oder dann lese ich, was auch immer die Adresse hier sagt, das Ergebnis

01:04:10.010 --> 01:04:11.310
davon wird hier wieder ausgegeben.

01:04:15.000 --> 01:04:16.000
Wie schließe ich es zusammen?

01:04:16.220 --> 01:04:18.500
Ich habe von vorhin ja einmal noch meinen Registerpfeil.

01:04:20.720 --> 01:04:22.400
Ich habe meine beiden Leseports.

01:04:24.660 --> 01:04:26.660
Jetzt muss ich erst die Adressberechnung machen.

01:04:27.640 --> 01:04:29.220
Für die Adressberechnung kann ich die ALU nehmen.

01:04:29.960 --> 01:04:32.360
Für die Adressberechnung brauchte ich ein Basisregister.

01:04:32.620 --> 01:04:34.200
Das war jetzt quasi nur das eine der beiden.

01:04:34.280 --> 01:04:41.820
Das war das Basisregister plus das Sign Extended Immediate.

01:04:42.440 --> 01:04:44.000
Jetzt muss ich das Immediate noch holen.

01:04:44.060 --> 01:04:45.420
Wo kommt das Immediate her?

01:04:45.940 --> 01:04:47.320
Das kommt aus dem Befehl raus.

01:04:47.500 --> 01:04:49.780
Das ist das 32-Bit-Befehl.

01:04:50.240 --> 01:04:54.360
Ein paar der Bits sind die Adresse der Register und ein paar der Bits

01:04:54.360 --> 01:04:57.560
waren halt eben das Immediate, was ich jetzt hier auf 200-Bit

01:04:57.560 --> 01:05:00.220
-Vorzeichen erweitern kann, was ich dann auch als Input an die ALU

01:05:00.220 --> 01:05:00.940
ranführen kann.

01:05:02.020 --> 01:05:05.820
Danach kann ich die Adressberechnung durchführen und kann quasi das

01:05:05.820 --> 01:05:08.600
Ergebnis ist dann, ja, die Adresse kommt dann aus der ALU raus, also

01:05:08.600 --> 01:05:10.080
die macht Additionen als Berechnung.

01:05:10.880 --> 01:05:13.200
Ergebnis ist, die Adresse gebe ich an den Datenspeicher.

01:05:14.380 --> 01:05:17.400
Und jetzt je nachdem, ob es ein Lade- oder Speicherbefehl ist, wenn es

01:05:17.400 --> 01:05:20.940
ein Speicherbefehl ist, also speichern im Sinne von aus dem Register

01:05:20.940 --> 01:05:25.520
-Feil in den Hauptspeicher, dann muss ich quasi aus dem Register-Feil

01:05:25.520 --> 01:05:26.940
ein Register lesen.

01:05:27.040 --> 01:05:29.160
Das ist das zweite, was in dem Befehl codiert worden ist.

01:05:29.840 --> 01:05:34.920
Und das lese ich und gebe als Input hier an den Datenspeicher.

01:05:35.120 --> 01:05:37.100
Dann wird das quasi an diese Adresse geschrieben.

01:05:37.520 --> 01:05:40.080
Das Signal wird dann vom Steuerwerk noch auf 1 gesetzt, also dass es

01:05:40.080 --> 01:05:41.080
klar geschrieben werden soll.

01:05:42.960 --> 01:05:46.940
Wohingegen, wenn es ein Lesebefehl war, brauche ich ja hier nur die

01:05:46.940 --> 01:05:47.360
Adresse.

01:05:47.540 --> 01:05:50.680
Die Adresse brauche ich in jedem Fall mit dem Signal, es soll bitte

01:05:50.680 --> 01:05:51.440
gelesen werden.

01:05:51.580 --> 01:05:55.460
Und dann kommt das Ergebnis, was gelesen wurde, hinten raus und wird

01:05:55.460 --> 01:05:58.300
dann ins Register-Feil reingeschrieben.

01:05:58.300 --> 01:06:05.400
Und jetzt wird das zweite Register, was im Befehl codiert wurde, nicht

01:06:05.400 --> 01:06:08.340
an die Leseadresse gegeben, sondern es wird an die Schreibadresse

01:06:08.340 --> 01:06:11.800
gegeben, damit das Zuschreibende an der richtigen Adresse im Register

01:06:11.800 --> 01:06:12.460
-Feil landet.

01:06:13.400 --> 01:06:16.620
Dann habe ich quasi Lese- und Schreibbefehle implementiert.

01:06:20.380 --> 01:06:24.500
Wie schon gesagt, ich kann diesen I-Typ-Befehl auch für andere Sachen

01:06:24.500 --> 01:06:27.380
benutzen, zum Beispiel für Verzweigungen, für Sprünge.

01:06:30.000 --> 01:06:34.960
Bei Sprüngen muss ich auch irgendwie gucken, im Sinne von rechnen.

01:06:35.480 --> 01:06:41.340
Zum einen muss ich das Sprung-Ziel ausrechnen, also ich habe hier zum

01:06:41.340 --> 01:06:43.540
Beispiel wieder das 16-Bit-Immediate.

01:06:43.940 --> 01:06:46.680
Das ist jetzt aber so zu verstehen, dass es halt kein Immediate ist

01:06:46.680 --> 01:06:49.440
für eine Adressberechnung, sondern naja, es ist auch eine

01:06:49.440 --> 01:06:50.080
Adressberechnung.

01:06:50.080 --> 01:06:52.320
Es hat nur keine Adresse für Daten, sondern die Adresse für den

01:06:52.320 --> 01:06:52.660
Befehl.

01:06:54.520 --> 01:06:58.400
Und es ist auch wieder eine Basis, in dem Fall ist das das

01:06:58.400 --> 01:06:59.860
Basisregister der Program-Counter.

01:07:00.260 --> 01:07:03.640
Also sprich, der Program-Counter, das ist der Befehl, also das zeigt

01:07:03.640 --> 01:07:06.260
auf den Befehl, den wir jetzt ausführen, und dann sollen wir relativ

01:07:06.260 --> 01:07:10.800
dazu von hier aus 20 Befehle weiterspringen und diese 20 würde dann

01:07:10.800 --> 01:07:12.640
quasi in einem Offset drinstehen.

01:07:12.980 --> 01:07:15.860
Also muss ich irgendwie eine Rechnung machen, die Program-Counter plus

01:07:15.860 --> 01:07:16.520
Offset rechnet.

01:07:17.680 --> 01:07:20.820
So, jetzt habe ich aber noch hier in dem Fall eine bedingte

01:07:20.820 --> 01:07:21.580
Verzweigung.

01:07:22.140 --> 01:07:24.720
Und bedingt heißt immer, ich muss auch irgendwie eine Bedingung

01:07:24.720 --> 01:07:25.400
ausrechnen.

01:07:25.920 --> 01:07:29.180
In dem Fall habe ich halt eben, dass der Sprung dann durchgeführt

01:07:29.180 --> 01:07:33.460
werden soll, wenn diese beiden Register identisch einen Inhalt haben,

01:07:33.560 --> 01:07:34.420
also equal sind.

01:07:35.340 --> 01:07:37.860
Das heißt, ich muss nicht nur die Adressberechnung machen, sondern ich

01:07:37.860 --> 01:07:40.420
muss zusätzlich die beiden Register vergleichen.

01:07:41.940 --> 01:07:44.860
Das ist jetzt irgendwie lästig, weil ich bräuchte für beides die ALU.

01:07:45.800 --> 01:07:47.800
Also muss ich irgendwie gucken, dass ich das hinkriege, entweder indem

01:07:47.800 --> 01:07:49.740
ich es nacheinander mache, aber das wollen wir ja nicht.

01:07:50.040 --> 01:07:51.500
Also brauche ich wahrscheinlich mehr Hardware dafür.

01:07:55.200 --> 01:07:58.720
Eine Sache werden wir gleich noch sehen, und zwar der Offset hier, der

01:07:58.720 --> 01:08:00.160
wird verschoben werden.

01:08:00.160 --> 01:08:05.280
Und zwar der Offset hier ist, ich meine, um auf den nächsten Befehl,

01:08:05.400 --> 01:08:10.560
Assemblerbefehl, Maschinenbefehl zu zeigen, der ist ja bei MIPS word

01:08:10.560 --> 01:08:14.160
-aligned, im Sinne von jeder Befehl ist 4-Byte groß, 32-Bit groß.

01:08:14.780 --> 01:08:16.760
Der nächste Befehl ist 4-Byte später.

01:08:17.220 --> 01:08:20.820
Sollte ich jetzt als Offset reinschreiben, springe 4-Byte, oder soll

01:08:20.820 --> 01:08:22.880
ich reinschreiben, springe ein Befehl?

01:08:22.880 --> 01:08:28.060
Ich springe ein Befehl, ist praktisch, weil dann brauche ich 2-Bit

01:08:28.060 --> 01:08:31.900
weniger sozusagen, oder umgekehrt, ich kann 2 hoch 2 mal mehr hier

01:08:31.900 --> 01:08:32.520
reinschreiben.

01:08:33.000 --> 01:08:36.020
Wenn die Zahl hier dritten jetzt aber interpretiert werden soll als

01:08:36.020 --> 01:08:39.480
Anzahl Befehle, die vorwärts oder rückwärts gesprungen werden sollen,

01:08:39.960 --> 01:08:43.500
dann muss ich ja für die Adressberechnung, also was ist der Program

01:08:43.500 --> 01:08:47.120
Counter Wert, muss ich für die Adressberechnung wieder umrechnen von

01:08:47.120 --> 01:08:51.200
wieviel Befehle auf wieviel Bytes soll ich springen, was gerade ein

01:08:51.200 --> 01:08:53.460
Shift bei 2 ist, also einfach 2 Nullen hinten dran hängen.

01:08:53.520 --> 01:08:55.460
Das ist keine echte Berechnung, einfach nur 2 Nullen hinten dran

01:08:55.460 --> 01:08:57.080
hängen, das sehen wir gleich auch noch.

01:08:57.140 --> 01:09:00.420
Aber das ist hier dieses 2-Bit nach links verschieben, muss ich genau

01:09:00.420 --> 01:09:04.000
tun, um aus Anzahl Befehle auf Anzahl Bytes zu kommen, die der Program

01:09:04.000 --> 01:09:05.280
Counter Wert geändert werden soll.

01:09:06.020 --> 01:09:08.140
So, das ist der Befehl, den wir durchführen wollen.

01:09:10.780 --> 01:09:19.700
Wir haben 9 Adder, das alte Register File, in den neuen Adder kommt

01:09:19.700 --> 01:09:24.580
jetzt nicht der Program Counter Wert rein, sondern der Program Counter

01:09:24.580 --> 01:09:25.560
Plus 4 Wert.

01:09:26.160 --> 01:09:27.700
Warum Program Counter Plus 4?

01:09:27.700 --> 01:09:32.180
Naja, weil wir in der Befehlshohe Phase bereits den Program Counter um

01:09:32.180 --> 01:09:33.420
4 hochgezählt haben.

01:09:33.840 --> 01:09:36.900
Das haben wir schon gemacht, also lässt sich jetzt gar nicht mehr so

01:09:36.900 --> 01:09:37.420
einfach ändern.

01:09:37.960 --> 01:09:41.180
Da muss man einfach dann aufpassen, nicht man, sondern der Assembler,

01:09:41.380 --> 01:09:46.780
der die Adressen ausrechnet, der muss aufpassen, wenn ich quasi, sagen

01:09:46.780 --> 01:09:50.080
wir mal, 10 Befehle weiterspringen will, dann muss er quasi einen

01:09:50.080 --> 01:09:55.280
neuen reinschreiben, weil ich ja diesen Plus 1 Befehl schon getan

01:09:55.280 --> 01:09:55.580
habe.

01:09:55.720 --> 01:09:59.740
Da muss man, oft bei One ist ärgerlich zu finden, aber da muss man

01:09:59.740 --> 01:10:00.340
halt eben aufpassen.

01:10:01.640 --> 01:10:04.180
So, der Adder würde das neue Sprungziel ausrechnen, das muss dann

01:10:04.180 --> 01:10:06.120
irgendwie noch in den Program Counter wieder reinkommen.

01:10:08.240 --> 01:10:11.800
Aus dem Befehl raus holen wir uns auf jeden Fall mal die beiden

01:10:11.800 --> 01:10:14.620
Register, die wir ja vergleichen sollen.

01:10:15.340 --> 01:10:18.200
Die legen wir ins Register File an, da kommen dann die Daten raus.

01:10:19.080 --> 01:10:23.260
Erstmal holen wir uns die 16-Bit Offset raus, machen wie vorhin auch

01:10:23.260 --> 01:10:27.120
die Vorzeichenerweiterung auf eine 32-Bit Zahl, kann positiv, kann

01:10:27.120 --> 01:10:27.720
negativ sein.

01:10:28.460 --> 01:10:31.860
Dieses Shift links ist keine Berechnung in dem Sinn, also dafür wird

01:10:31.860 --> 01:10:33.560
kein einziges Data benötigt.

01:10:33.980 --> 01:10:41.300
Das ist einfach nur, was in dem Wort hier, in dem 32-Bit-Wort, Bit 2

01:10:41.300 --> 01:10:43.540
ist, ist in dem hier Bit 0.

01:10:43.720 --> 01:10:46.040
Was hier Bit 3 ist, ist hier Bit 1.

01:10:46.040 --> 01:10:50.340
Also ich schmeiße einfach hier 2 Bit weg und lege die Kabel, die

01:10:50.340 --> 01:10:51.400
Leitungen ein bisschen anders.

01:10:51.660 --> 01:10:53.500
Weil da brauche ich keinen Gatter für, ich brauche keinen Und-Gatter

01:10:53.500 --> 01:10:53.960
oder sowas.

01:10:54.060 --> 01:10:56.140
Ich brauche überhaupt nichts, ich brauche nur die Leitungen anders

01:10:56.140 --> 01:10:56.720
anzuschließen.

01:10:57.600 --> 01:10:58.640
Also das ist geschenkt.

01:10:59.280 --> 01:11:02.620
Muss man sich einfach merken, also Shift mit der Konstanten ist aus

01:11:02.620 --> 01:11:04.200
Hardware -Sicht keine Berechnung.

01:11:04.540 --> 01:11:07.020
Shift mit einer variablen Zahl ist eine sehr, sehr unangenehme

01:11:07.020 --> 01:11:07.920
Berechnung aus Hardware-Sicht.

01:11:10.300 --> 01:11:13.880
Okay, ich habe jetzt quasi den Offset berechnet, den ich addieren

01:11:13.880 --> 01:11:16.260
möchte, falls gesprungen werden soll.

01:11:16.500 --> 01:11:19.260
Jetzt muss ich noch herausfinden, soll denn gesprungen werden.

01:11:19.380 --> 01:11:23.080
Und dafür habe ich jetzt die beiden Register, die verglichen werden

01:11:23.080 --> 01:11:23.400
sollen.

01:11:23.520 --> 01:11:26.080
Adressen ins Register-File, die beiden Ausgaben kommen hier raus.

01:11:26.580 --> 01:11:31.160
Ich mache den Vergleich und jetzt habe ich hier eine neue Ausgabe, die

01:11:31.160 --> 01:11:37.220
ALU berechnet nicht nur ein 32-Bit-Ausgabe-Ergebnis, sondern die ALU

01:11:37.220 --> 01:11:39.580
hat auch noch verschiedene Flags.

01:11:40.040 --> 01:11:44.480
Wir hatten das in einem früheren Beispiel irgendwo mal gesehen, da gab

01:11:44.480 --> 01:11:48.100
es dieses Carry-Flag und Overflow und Negative und Zero.

01:11:48.520 --> 01:11:51.400
Das wäre jetzt das Zero-Flag, was im Wesentlichen einfach nur sagt,

01:11:51.820 --> 01:11:53.820
war die Ausgabe Null oder nicht?

01:11:54.440 --> 01:11:56.560
Wir haben uns überlegt, wie vergleicht man zwei Zahlen?

01:11:56.720 --> 01:11:58.680
Das ist einfach, man subtrahiert sie voneinander und wenn das Ergebnis

01:11:58.680 --> 01:11:59.740
Null ist, dann waren sie gleich.

01:11:59.880 --> 01:12:01.920
Und wenn sie nicht Null sind, dann war die eine größer oder die

01:12:01.920 --> 01:12:02.180
andere.

01:12:02.180 --> 01:12:04.740
Dann wird eben das Greater- oder das Greater-Equal-Flag oder sonst

01:12:04.740 --> 01:12:05.540
irgendwie sowas gesetzt.

01:12:07.180 --> 01:12:11.460
In dem Fall nutzen wir dieses Zero-Flag, was sowieso bestimmt wird, um

01:12:11.460 --> 01:12:14.680
dann zu entscheiden, soll gesprungen werden oder nicht.

01:12:19.280 --> 01:12:21.220
Jetzt müssen wir so langsam mal anfangen.

01:12:22.060 --> 01:12:25.200
Jetzt haben wir quasi die einzelnen Befehle angeschaut, also den R-Typ

01:12:25.200 --> 01:12:28.000
und den I-Typ und den J-Typ haben wir ignoriert, aber lassen wir mal

01:12:28.000 --> 01:12:28.360
die beiden.

01:12:28.840 --> 01:12:31.340
Jetzt müssen wir anfangen, das Ganze zusammenzubauen.

01:12:31.340 --> 01:12:35.680
Weil der R-Typ hat ja die ALU für etwas anderes benutzt, als was der I

01:12:35.680 --> 01:12:36.300
-Typ gemacht hat.

01:12:36.380 --> 01:12:39.660
Und auch im I-Typ hat der Sprungbefehl etwas anderes gemacht, als der

01:12:39.660 --> 01:12:40.560
Ladespeicherbefehl.

01:12:42.840 --> 01:12:43.700
Gucken wir mal.

01:12:44.060 --> 01:12:49.440
Wir haben jetzt hier Register-File, Befehl angeschlossen.

01:12:50.760 --> 01:12:52.420
Wir haben die ALU hier dran.

01:12:53.060 --> 01:12:56.740
Hier haben wir jetzt das normale Ergebnis der Berechnung.

01:12:57.180 --> 01:13:00.000
Und jetzt haben wir das Zero-Flag hier nochmal extra hingemalt, um es

01:13:00.000 --> 01:13:01.440
zu unterscheiden, weil wir brauchen es ja nicht im Einen, wir brauchen

01:13:01.440 --> 01:13:02.600
es nur durch das Zero-Flag.

01:13:03.380 --> 01:13:05.940
Und was wir jetzt neu haben, ist hier so ein Multiplexer.

01:13:06.500 --> 01:13:10.000
Und der Multiplexer kennt man ja, der lässt quasi einen der beiden

01:13:10.000 --> 01:13:11.760
Inputs den Ausgang durch.

01:13:11.980 --> 01:13:15.860
Und welchen der beiden Inputs wird von irgendeinem Steuersignal

01:13:15.860 --> 01:13:16.200
bestimmt.

01:13:16.420 --> 01:13:18.480
Und da müssen wir uns später noch angucken, wo kommt dieses

01:13:18.480 --> 01:13:19.700
Steuersignal eigentlich her.

01:13:19.700 --> 01:13:23.160
Zumindest haben wir jetzt die Möglichkeit, hier oben das Register ist

01:13:23.160 --> 01:13:26.780
quasi direkt an die ALU angeschlossen, hier unten das zweite Register

01:13:26.780 --> 01:13:31.940
aus dem Register-File gelesen werden kann, ist entweder dieses

01:13:31.940 --> 01:13:35.160
Register, der gelesene Wert davon, kann an die ALU angeschlossen

01:13:35.160 --> 01:13:39.800
werden, oder das Sign Extended Immediate kann an die ALU angeschlossen

01:13:39.800 --> 01:13:40.160
werden.

01:13:41.800 --> 01:13:50.320
Das wäre jetzt für den Ladespeicher vom I-Typ und den allgemeinen R

01:13:50.320 --> 01:13:52.080
-Typ.

01:13:54.060 --> 01:13:58.220
Ergebnis ist dann entweder bei Lade- und Speicherbefehl ist das

01:13:58.220 --> 01:14:02.700
Ergebnis hier die Adresse, und im Fall eines Lesebefehls hätten wir

01:14:02.700 --> 01:14:07.400
dann quasi das gelesene, was hier über den MUX ans Register-File in

01:14:07.400 --> 01:14:11.660
den Schreibeingang angeschlossen werden kann, oder falls es überhaupt

01:14:11.660 --> 01:14:15.660
kein Ladespeicherbefehl war, falls es eine ALU-Operation war, kann ich

01:14:15.660 --> 01:14:19.120
hier quasi das Ergebnis von der ALU direkt abzweigen, also nicht in

01:14:19.120 --> 01:14:22.880
den Speicher rein, und kann es durch den MUX direkt ins Register-File

01:14:22.880 --> 01:14:23.300
zurückschreiben.

01:14:24.240 --> 01:14:27.340
Also quasi das, was geschrieben wird ins Register-File, ist entweder

01:14:27.340 --> 01:14:33.080
vom Datenspeicher im Fall eines Ladebefehls oder von der ALU im Fall

01:14:33.080 --> 01:14:37.720
eines Arithmetik-Logik-Befehls, oder im Fall eines Speicherbefehls

01:14:37.720 --> 01:14:38.600
wird einfach gar nichts geschrieben.

01:14:38.780 --> 01:14:40.900
Also das gar nichts zu schreiben kann ich hier immer noch, hier ist

01:14:40.900 --> 01:14:42.920
auch ein Steuersignal, mit dem ich sagen kann, es soll gar nichts

01:14:42.920 --> 01:14:43.580
geschrieben werden.

01:14:44.840 --> 01:14:49.060
Okay, also habe ich jetzt neue Multiplexer, die ich ansteuern muss, um

01:14:49.060 --> 01:14:52.060
halt eben entweder den Befehl oder den Befehl mit der Hardware

01:14:52.060 --> 01:14:52.860
durchführen zu können.

01:14:58.120 --> 01:14:59.620
Das ist dasselbe nochmal.

01:15:00.920 --> 01:15:06.140
Und jetzt noch dazu angeschlossen die allgemeine Befehls-Holphase, die

01:15:06.140 --> 01:15:08.940
wir vorne dran hatten, im Allgemeinen für alle erstmal.

01:15:09.640 --> 01:15:12.080
Also sprich, bisher hätten wir jetzt angeguckt, wo kommt der Befehl

01:15:12.080 --> 01:15:14.240
her, also hier kommt der Befehl rein, und wo kommt er her?

01:15:14.320 --> 01:15:16.760
Naja, aus dem Befehls-Speicher, da habe ich vorne den Programme

01:15:16.760 --> 01:15:21.660
-Counter, den Plus-4-Addierer, also die Holstufe sozusagen, vorne noch

01:15:21.660 --> 01:15:22.220
angeschlossen.

01:15:23.620 --> 01:15:25.960
Einfach nur wieder ein Stück zusammengebaut.

01:15:28.990 --> 01:15:32.050
Und jetzt müssen wir die Holstufe nochmal aufbrechen, denn jetzt gab

01:15:32.050 --> 01:15:33.010
es ja noch die Sprungbefehle.

01:15:33.470 --> 01:15:35.910
Und um die Sprungbefehle in die Sache reinzukriegen, müssen wir ja

01:15:35.910 --> 01:15:38.890
irgendwie in der Lage sein, den Programme-Counter-Wert zu

01:15:38.890 --> 01:15:39.610
überschreiben.

01:15:39.930 --> 01:15:42.730
Hier war ja quasi in dem alten, dass der Programme-Counter-Wert immer

01:15:42.730 --> 01:15:45.790
aus dem alter Wert Plus-4 kam, damit kann ich ja keine Sprünge machen.

01:15:45.870 --> 01:15:48.050
Ich muss ja hier jetzt auch mal in der Lage sein, andere Werte

01:15:48.050 --> 01:15:49.790
reinzuschreiben, um einen Sprung machen zu können.

01:15:50.790 --> 01:15:51.690
Warum trenne ich das nochmal auf?

01:15:52.030 --> 01:15:54.670
Aber ich habe auf jeden Fall den Programme-Counter und den Plus-4 für

01:15:54.670 --> 01:15:56.730
die normalen Nicht-Sprungbefehle behalte ich.

01:15:57.730 --> 01:16:00.330
Und jetzt baue ich folgenden Teil dazu.

01:16:01.670 --> 01:16:05.870
Einmal die Vorzeichenerweiterung, das Shift-2, die wir schon

01:16:05.870 --> 01:16:09.470
besprochen hatten, um aus Anzahl Befehle Anzahl Bytes zu machen, die

01:16:09.470 --> 01:16:10.270
addiert werden müssen.

01:16:11.130 --> 01:16:18.790
Dann PC-Plus-4 als Input an den Addierer, der quasi das PC-relative

01:16:18.790 --> 01:16:21.810
Sprung -Ziel dazu addiert.

01:16:21.810 --> 01:16:26.530
Und jetzt wieder ein Multiplexer, der entweder das PC-Plus-4

01:16:26.530 --> 01:16:30.190
durchlässt, im Fall, dass es kein Sprung ist, oder dass es ein

01:16:30.190 --> 01:16:32.910
Sprungbefehl ist, aber der Sprung soll nicht genommen werden, weil das

01:16:32.910 --> 01:16:35.570
Branch -Equal zum Beispiel sagt, die sind nicht gleich, dann soll auch

01:16:35.570 --> 01:16:36.290
nicht gesprungen werden.

01:16:36.790 --> 01:16:40.950
Dann wird das PC-Plus-4 durchgelassen, oder das neue Sprung-Ziel aus

01:16:40.950 --> 01:16:48.250
der Berechnung PC plus geschifftetes, zahlen-extendedes, immediat soll

01:16:48.250 --> 01:16:48.950
durchgelassen werden.

01:16:48.950 --> 01:16:52.290
Und das geht dann wieder zurück, Leitung, Leitung, Leitung, in den

01:16:52.290 --> 01:16:54.370
Program -Counter rein, und das ist dann der Input für den nächsten

01:16:54.370 --> 01:16:58.510
Befehl, also die Adresse des nächsten Befehls und die Basisadresse für

01:16:58.510 --> 01:16:59.730
die nächste Adressberechnung.

01:17:04.920 --> 01:17:09.000
Okay, jetzt haben wir die oberen beiden Befehlsformate erledigt, den

01:17:09.000 --> 01:17:12.900
unteren ignorieren wir wirklich, da kommt nicht mehr viel hinzu.

01:17:13.240 --> 01:17:16.720
Das ist einfach nur ein unbedingter Sprung, also kein Vergleich, da

01:17:16.720 --> 01:17:17.460
ist nichts zu tun.

01:17:17.460 --> 01:17:21.580
Und die Sign-Extension ist halt eben nicht eine 16-Bit-Zahl, die auf

01:17:21.580 --> 01:17:24.860
eine 32-Bit-Zahl erweitert werden muss, sondern ist halt eine 26-Bit

01:17:24.860 --> 01:17:27.140
-Zahl, die auf eine 32-Bit-Zahl erweitert werden muss.

01:17:27.260 --> 01:17:31.520
Ansonsten ist da nichts zu tun, also für das Befehlsformat ist nichts

01:17:31.520 --> 01:17:32.480
weiter zu berücksichtigen.

01:17:33.320 --> 01:17:37.680
Jetzt gucken wir uns noch an, wie die Adressen hier anzuschließen

01:17:37.680 --> 01:17:38.020
sind.

01:17:41.360 --> 01:17:42.820
Und wir gucken uns die Steuerung an.

01:17:42.820 --> 01:17:45.980
Erstmal die Adressen, das ist das gleiche wie vorhin und da kann man

01:17:45.980 --> 01:17:48.780
sehen, was ist mit dem Befehl, das ist hier wirklich ein 32-Bit

01:17:48.780 --> 01:17:53.020
-Befehl, der hier rauskommt und davon gehen immer die unteren 16-Bit

01:17:53.020 --> 01:17:55.400
einfach direkt hart verdrahtet, gehen die in die

01:17:55.400 --> 01:17:59.100
Vorzeichenerweiterung, entweder für Immediate-Berechnungen oder für

01:17:59.100 --> 01:18:00.000
Sprungbefehle.

01:18:01.120 --> 01:18:07.700
Wir haben je nachdem, welcher Befehl ausgeführt wird, haben wir in der

01:18:07.700 --> 01:18:10.900
Schreibadresse jetzt mal das eine, mal das andere anliegen.

01:18:10.900 --> 01:18:11.940
Das ist unterschiedlich.

01:18:12.380 --> 01:18:17.060
Es gab jetzt die R-Befehle, wo es quasi drei verschiedene Register

01:18:17.060 --> 01:18:20.840
adressiert werden konnten und die hatten dann quasi hier ein Lese

01:18:20.840 --> 01:18:27.000
-Port, also in den 21-25 ein Lese-Port, 16-20 noch eine Lese-Adresse

01:18:27.000 --> 01:18:30.240
und 11-15 hatten sie dann quasi die Schreibadresse.

01:18:30.900 --> 01:18:33.660
Und ein anderer Befehlstyp, der hatte halt eben, schauen wir mal kurz,

01:18:33.960 --> 01:18:37.540
das wäre quasi genau die hier, der hat diese drei Register, die

01:18:37.540 --> 01:18:38.240
adressiert werden können.

01:18:38.920 --> 01:18:43.260
Und das Rd, das Zielregister war halt ungeschickterweise, würde ich

01:18:43.260 --> 01:18:47.600
sagen, gerade hier so gelegt worden, sodass bei dem Befehl hier das Rd

01:18:47.600 --> 01:18:49.600
nicht mehr vorhanden ist, weil es Teil von dem Immediate ist.

01:18:49.980 --> 01:18:55.040
Jetzt muss halt eben einer von den beiden die Schreibadresse vom

01:18:55.040 --> 01:18:55.840
Register feil werden.

01:18:56.400 --> 01:18:57.600
Können wir noch mal gucken, welcher es jetzt ist.

01:18:57.680 --> 01:18:58.460
Ich habe vorhin da mal geraten.

01:19:00.160 --> 01:19:01.800
Offenbar 20-16.

01:19:02.620 --> 01:19:07.060
Also der wäre dann quasi das Schreibziel und der würde gelesen werden.

01:19:07.060 --> 01:19:09.780
Kann man jetzt hier wunderbar sehen, der ist hart verdrahtet beim

01:19:09.780 --> 01:19:14.340
Lesen dran und der könnte hier auch über den Mux an die Schreibadresse

01:19:14.340 --> 01:19:15.060
rangehen.

01:19:16.020 --> 01:19:18.700
Also man kann hier sehen, dass es eigentlich relativ elegant ist.

01:19:19.380 --> 01:19:22.460
Aus dem Befehlswort werden einfach direkt die Bits an die richtige

01:19:22.460 --> 01:19:24.580
Sache angeschlossen und passt, funktioniert.

01:19:25.380 --> 01:19:29.380
Jetzt haben wir aber hier oben noch einen Teil, der heißt Steuerung.

01:19:30.320 --> 01:19:32.860
Da gehen auch noch ein paar Bits dran und zwar im Wesentlichen geht da

01:19:32.860 --> 01:19:33.520
der Opcode hin.

01:19:33.520 --> 01:19:36.200
Und der Opcode muss jetzt irgendwie entscheiden, welcher Befehl ist

01:19:36.200 --> 01:19:36.920
das jetzt eigentlich.

01:19:37.620 --> 01:19:40.640
Und der muss irgendwie entscheiden für jeden dieser Muxe, was soll der

01:19:40.640 --> 01:19:44.700
Mux eigentlich tun, was soll die ALU tun, soll der schreiben, lesen,

01:19:44.760 --> 01:19:45.400
gar nichts machen.

01:19:45.780 --> 01:19:48.100
Das muss alles noch entschieden werden und das macht halt eben die

01:19:48.100 --> 01:19:48.520
Steuerung.

01:19:57.390 --> 01:19:59.670
Nochmal kurz, der Teil kommt dazu.

01:20:00.290 --> 01:20:01.850
Das ist die Steuerung von der ALU.

01:20:02.850 --> 01:20:07.650
Die hängt jetzt hier erstmal, ist ein bisschen blöd gemalt, die hängt

01:20:07.650 --> 01:20:10.650
auch von dem Befehl direkt, nicht von dem Immediate, das sieht jetzt

01:20:10.650 --> 01:20:15.170
doof aus, die hängt von dem Befehl direkt ab und das war das ALU

01:20:15.170 --> 01:20:15.670
-Steuersignal.

01:20:15.770 --> 01:20:18.530
Wenn wir mal kurz zurückgehen, das ist hier dieses Function Bit.

01:20:18.890 --> 01:20:24.010
Also dieses Function Bit, was sagt, wenn es der R-Typ ist, dann steht

01:20:24.010 --> 01:20:27.470
in den Bits 0 bis 5 drin, welche ALU-Operation gemacht werden soll.

01:20:27.470 --> 01:20:31.270
Soll es addieren, soll es shiften, soll es oder verknöpfen.

01:20:32.850 --> 01:20:37.550
Also haben wir jetzt hier die 5 Bit, die jetzt irgendwie mit einer

01:20:37.550 --> 01:20:42.130
Logik die Steuerung für die ALU erzeugen, aber das war ja noch unter

01:20:42.130 --> 01:20:45.310
der Annahme, wenn es der R-Typ ist, also müssen wir irgendwie noch

01:20:45.310 --> 01:20:48.050
rausfinden, ist es der R-Typ, also da fehlt noch ein bisschen was.

01:20:49.410 --> 01:20:50.410
Und zwar nämlich genau das hier.

01:20:50.850 --> 01:20:54.870
Hier oben haben wir den Op-Code dran, die obersten Bits von dem Befehl

01:20:54.870 --> 01:20:58.490
und der Op-Code findet dann raus, ist es ein R-Typ, ist es ein I-Typ,

01:20:58.570 --> 01:20:59.390
ist es ein J-Typ.

01:20:59.810 --> 01:21:02.990
Und das wäre dann quasi ein weiterer Input für diesen Logik-Block und

01:21:02.990 --> 01:21:06.870
der zusammen würde dann quasi das Steuersignal für die ALU erzeugen.

01:21:07.130 --> 01:21:10.690
Weil es ist ja auch, also wenn es ein R-Typ ist, dann sagt er hier,

01:21:10.750 --> 01:21:14.790
was gerechnet werden soll, aber wenn es kein R-Typ ist, der I-Typ

01:21:14.790 --> 01:21:16.530
musste bei der ALU ja auch was rechnen.

01:21:16.670 --> 01:21:17.850
Da musste ja auch was getan werden.

01:21:17.970 --> 01:21:22.170
Also auch für den I-Typ muss irgendwie die ALU eine Funktion ausführen

01:21:22.170 --> 01:21:24.750
und darum braucht man hier einen Teil der Steuerung, die ALU

01:21:24.750 --> 01:21:28.290
-Steuerung, die halt eben genau das bestimmt, je nachdem welches

01:21:28.290 --> 01:21:31.790
Befehlsformat es ist und was der Befehl im Einzelnen genau will, wird

01:21:31.790 --> 01:21:34.130
bestimmt, was soll die ALU eigentlich rechnen, soll sie addieren oder

01:21:34.130 --> 01:21:35.470
shiften oder was anderes.

01:21:38.330 --> 01:21:41.510
Dann haben wir ein paar der Steuersignale, die einfach hier benutzt

01:21:41.510 --> 01:21:45.070
werden, um zu sagen, wie sollen die Adressen abortiert werden, welche

01:21:45.070 --> 01:21:46.490
von den beiden soll durchgelassen werden.

01:21:46.650 --> 01:21:50.170
Also das Schreibziel beim R-Typ wären die Schreibbits halt eben das

01:21:50.170 --> 01:21:55.570
hier und beim I-Typ wären die Schreibbits, also wäre die Adresse, das

01:21:55.570 --> 01:21:58.770
Schreibport des Register-Files hier zu finden, und beim I-Typ wären

01:21:58.770 --> 01:21:59.570
sie hier zu finden.

01:22:01.510 --> 01:22:02.410
Dann noch mehr Bits.

01:22:03.070 --> 01:22:06.830
Wir brauchen, ob das Register-File überhaupt schreiben soll.

01:22:07.170 --> 01:22:08.870
Lesen kann man immer, lesen tut nicht weh.

01:22:09.190 --> 01:22:10.190
Also es braucht höchstens Strom.

01:22:10.670 --> 01:22:13.770
Aber schreiben, wenn ich einen Befehl habe, wie zum Beispiel einen

01:22:13.770 --> 01:22:16.890
Store, also der in den Hauptspeicher schreibt, dann darf ich nicht ins

01:22:16.890 --> 01:22:17.750
Register -File schreiben.

01:22:17.750 --> 01:22:19.530
Ich würde ja irgendwas überschreiben.

01:22:19.690 --> 01:22:21.670
Dann muss ich das Schreiben abstellen können.

01:22:21.770 --> 01:22:23.330
Das kommt auch aus dem Steuerwerk hier oben raus.

01:22:24.170 --> 01:22:27.170
Ich habe für jeden Mux ein Steuersignal.

01:22:27.430 --> 01:22:32.230
Ich habe ein Steuersignal, das mir ausrechnen muss, soll das Register

01:22:32.230 --> 01:22:36.810
-Leseport durchgelassen werden oder soll das Sign-Excel-Demediate

01:22:36.810 --> 01:22:38.330
-Antialu durchgelassen werden.

01:22:38.870 --> 01:22:41.830
Ich brauche ein Steuersignal, was mir sagt, beim Datenspeicher,

01:22:42.450 --> 01:22:45.610
welches haben wir hier, soll eine Schreiboperation auf den

01:22:45.610 --> 01:22:46.850
Datenspeicher gemacht werden.

01:22:47.390 --> 01:22:50.170
Soll eine Leseoperation auf den Datenspeicher gemacht werden.

01:22:50.530 --> 01:22:53.530
Das würde jetzt quasi bei der Systemschnittstelle rausgehen.

01:22:53.650 --> 01:22:56.090
Da gab es ja Datenbus, Adressbus und Steuerbus.

01:22:56.330 --> 01:22:58.670
Und das wären quasi genau die Steuerleitungen.

01:22:58.850 --> 01:23:01.210
Soll es ein Schreibzugriff, soll es ein Lesezugriff oder soll es gar

01:23:01.210 --> 01:23:02.930
kein Zugriff auf den Hauptspeicher sein.

01:23:03.930 --> 01:23:07.350
Ich habe ein Steuersignal, was mir sagt, soll aus dem Hauptspeicher

01:23:07.350 --> 01:23:11.010
das Gelesene ins Register-File geschrieben werden oder soll das Alu

01:23:11.010 --> 01:23:12.590
-Register -File ins Register-File geschrieben werden.

01:23:13.250 --> 01:23:13.930
Was fehlt noch?

01:23:15.810 --> 01:23:18.190
Ah ja, hier unten habe ich noch ein Steuersignal.

01:23:18.470 --> 01:23:19.590
Das war der Sprung.

01:23:19.730 --> 01:23:23.830
Beim bedingten Sprung, da ist das Gratuale überschaubar, da kann man

01:23:23.830 --> 01:23:24.310
es hinmalen.

01:23:24.670 --> 01:23:25.810
Das war ja quasi die Bedingung.

01:23:25.910 --> 01:23:27.890
Wir wollen mal schauen, was dazu gekommen ist.

01:23:29.390 --> 01:23:32.390
Ich muss quasi entscheiden, soll ich das normale PC plus 4

01:23:32.390 --> 01:23:36.450
durchlassen, das kommt von hier, oder soll ich die

01:23:36.450 --> 01:23:38.910
Sprungzielberechnung durchlassen.

01:23:39.610 --> 01:23:42.990
Und die Sprungzielberechnung, den oberen, soll ich durchlassen, wenn

01:23:42.990 --> 01:23:49.850
von oben kommend aus der Steuereinheit, wenn es ein Sprungbefehl ist

01:23:49.850 --> 01:23:56.170
und wenn der Vergleich gesagt hat, equal zero, ja, es war equal zero.

01:23:56.330 --> 01:23:59.470
Und nur wenn beides der Fall ist, dann soll ich die Sprungadresse

01:23:59.470 --> 01:24:00.230
wirklich durchlassen.

01:24:02.910 --> 01:24:03.150
Gut.

01:24:04.150 --> 01:24:09.990
Wenn man diesen ganzen Kram jetzt aufmalen würde als Zustandsautomat,

01:24:09.990 --> 01:24:12.350
dann würde das Ganze so aussehen.

01:24:13.870 --> 01:24:17.810
Das gucken wir uns jetzt glücklicherweise nicht im Detail an, aber ich

01:24:17.810 --> 01:24:19.470
sag mal, es ist auch keine große Überraschung drin.

01:24:19.710 --> 01:24:23.110
Wir haben ja vom Inhalt verstanden, was wir steuern sollen.

01:24:23.670 --> 01:24:25.530
Jetzt muss man sich nur überlegen, dass man es hinkriegt.

01:24:26.410 --> 01:24:29.750
Es gibt also offenbar irgendeinen Zustand, in dem das Ganze anfängt,

01:24:29.910 --> 01:24:32.790
wo ein Befehl geholt werden soll, wo muss man die richtigen

01:24:32.790 --> 01:24:38.070
Steuersignale anlegen, damit der Befehlsspeicher auch den nächsten

01:24:38.070 --> 01:24:40.650
Befehl halt eben basierend auf der Adresse Rang schafft.

01:24:42.010 --> 01:24:45.770
Und dafür werden halt eben überall die Steuersignale von den

01:24:45.770 --> 01:24:48.210
verschiedenen Stufen, von den verschiedenen Muxen und was wir alles

01:24:48.210 --> 01:24:50.430
gesehen hatten, müssen irgendwie mit Werten belegt werden.

01:24:51.390 --> 01:24:54.870
Dann geht es in den Decoder rein und im Decoder haben wir jetzt den

01:24:54.870 --> 01:24:57.370
Befehl und im Decoder hängt es dann davon ab, ist es der Befehl, der

01:24:57.370 --> 01:25:00.350
Befehl oder der Befehl und je nachdem verzweigen wir jetzt in vier

01:25:00.350 --> 01:25:02.410
verschiedene Zustände.

01:25:03.110 --> 01:25:08.150
Zum Beispiel irgendein unbedingter Sprung, ein bedingter Sprung oder

01:25:08.150 --> 01:25:11.130
eine ALU-Ausführung oder ein Ladespeicherbefehl.

01:25:11.550 --> 01:25:15.170
Und da habe ich dann wiederum Unterzustände, die dann quasi das Laden,

01:25:15.310 --> 01:25:16.990
Speichern, Einzelnen steuern.

01:25:18.290 --> 01:25:20.550
Aber wie gesagt, da guckt man sich die Details nicht an.

01:25:21.310 --> 01:25:23.630
Im Allgemeinen sieht es auf jeden Fall immer so aus, jeder

01:25:23.630 --> 01:25:29.470
Zuschussautomat, dass ich quasi ein rein logisches Schaltnetz habe, wo

01:25:29.470 --> 01:25:31.270
ich nur kombinatorische Gatter drin habe.

01:25:31.810 --> 01:25:36.690
Die bekommen irgendeinen Input aus dem Assemblerbefehl, den ich gerade

01:25:36.690 --> 01:25:40.450
jetzt versuche auszuführen und die bekommen einen Input aus

01:25:40.450 --> 01:25:41.450
irgendeinem Zustand.

01:25:41.590 --> 01:25:42.710
Das ist hier das Statusregister.

01:25:44.030 --> 01:25:47.370
Und hier steht jetzt quasi drin von dem Zustandsautomaten, in welchem

01:25:47.370 --> 01:25:50.470
dieser verschiedenen, jeder Kreis ist ein Zustand, in welchen dieser

01:25:50.470 --> 01:25:51.950
Zustände bin ich denn gerade.

01:25:52.390 --> 01:25:55.590
Das wird quasi hier gespeichert und da kommt jetzt ein Input, in

01:25:55.590 --> 01:25:59.910
welchem Zustand bin ich, was bekomme ich für Inputs vom Befehl zum

01:25:59.910 --> 01:26:01.150
Beispiel, sonstige Inputs.

01:26:01.150 --> 01:26:05.570
Und daraus bestimme ich einmal hier den Nachfolgezustand, der dann

01:26:05.570 --> 01:26:08.730
quasi hier im Register wieder gespeichert wird und ich bestimme die

01:26:08.730 --> 01:26:12.010
ganzen Steuersignale für die Multiplexer und sonstiges in meiner

01:26:12.010 --> 01:26:12.870
Ausführungshardware.

01:26:16.130 --> 01:26:23.310
Gut, jetzt haben wir im Wesentlichen die einzelnen Komponenten von der

01:26:23.310 --> 01:26:24.870
MIPS -CPU aufgebaut.

01:26:26.090 --> 01:26:28.350
Nämlich, dass es eben eine ALU gibt, es gibt eine Speichereinheit,

01:26:28.350 --> 01:26:30.150
eine für Daten, eine für Befehle.

01:26:30.230 --> 01:26:32.730
Die waren jetzt hier auch hübsch sauber getrennt hingemalt.

01:26:33.270 --> 01:26:35.630
Eigentlich müssten die ja irgendwann später wieder zusammenkommen,

01:26:35.830 --> 01:26:38.750
bevor sie an den Systembus gehen, aber erstmal sind es zwei getrennte

01:26:38.750 --> 01:26:39.510
Speicher für uns.

01:26:39.950 --> 01:26:44.070
Wir haben die ganzen verschiedenen Register gesehen, zunächst

01:26:44.070 --> 01:26:45.570
Befehlsregister, nicht alle Register haben wir gesehen.

01:26:45.670 --> 01:26:47.470
Ein paar Register haben wir gesehen, die man auf jeden Fall braucht.

01:26:47.630 --> 01:26:48.950
Befehlsregister eigentlich das Einzige, oder?

01:26:50.070 --> 01:26:51.710
Aber das Ganze war noch nicht gepipelined.

01:26:51.810 --> 01:26:55.230
Das war jetzt nur, um diesen MIPS-Befehlssatz ausführen zu können.

01:26:56.610 --> 01:26:58.110
Diese Hardwarekomponenten braucht man halt eben.

01:27:00.130 --> 01:27:03.930
Und jetzt müssten wir das Ganze mal anfangen, so langsam zu pipeline,

01:27:04.090 --> 01:27:07.250
in verschiedene Pipeline-Stufen aufzuteilen.

01:27:07.610 --> 01:27:09.610
Das gucken wir auf jeden Fall mal am Anfang noch kurz an.

01:27:12.970 --> 01:27:16.310
Ich habe die Stufen früher schon mal genannt gehabt, anhand von

01:27:16.310 --> 01:27:18.650
lustigen Bildchen, und jetzt nochmal anhand von den

01:27:18.650 --> 01:27:20.350
Hardwarekomponenten, die wir gesehen haben.

01:27:20.610 --> 01:27:22.710
Mit den Namen, wie sie für gewöhnlich auch bezeichnet werden.

01:27:23.430 --> 01:27:26.950
Es gäbe quasi eine Instruction Fetch-Stufe, die sich halt eben genau

01:27:26.950 --> 01:27:31.610
um die Adressberechnung kümmert, und um Program Counter und Zugriff

01:27:31.610 --> 01:27:32.570
auf den Befehlsspeicher.

01:27:33.510 --> 01:27:35.730
Also das Ranholen des nächsten Befehls.

01:27:36.810 --> 01:27:41.830
Da gibt es, manche CPUs trennen das in zwei Stufen, manchmal ist es

01:27:41.830 --> 01:27:45.770
eine Stufe, die ist es eine, und die hat die Aufgaben Instruction

01:27:45.770 --> 01:27:48.770
Decode, rausfinden, was will der Befehl eigentlich tun.

01:27:49.250 --> 01:27:52.730
Und nachdem man das weiß, weiß man auch, welche Register man dafür

01:27:52.730 --> 01:27:57.490
laden soll, Register Fetch, das wäre quasi hier, also der Zugriff auf

01:27:57.490 --> 01:28:01.370
Operanden, und falls einem Operand eben eine Miete ist, dann das

01:28:01.370 --> 01:28:02.410
Vorzeichen erweitern davon.

01:28:04.410 --> 01:28:06.110
Danach kommt die eigentliche Rechnung.

01:28:07.290 --> 01:28:11.510
Das Addieren von der Adressberechnung, oder das ALU-Berechnen von der

01:28:11.510 --> 01:28:12.330
normalen Operation.

01:28:15.990 --> 01:28:19.990
Speicherzugriff, falls es ein Ladespeicherbefehl war.

01:28:20.590 --> 01:28:23.350
Hier habe ich jetzt einen neuen Rückwärtsfahrt, der aufgetaucht ist.

01:28:23.590 --> 01:28:26.910
Das war quasi das Ergebnis von der Adressberechnung, für den Fall

01:28:26.910 --> 01:28:27.710
eines Sprungbefehls.

01:28:29.510 --> 01:28:31.950
Und hier haben wir dann das Writeback, also das Zurückschreiben des

01:28:31.950 --> 01:28:38.470
Ergebnisses ins Register-File, je nachdem, aus der ALU kommend, oder

01:28:38.470 --> 01:28:40.570
aus dem Datenspeicher kommend.

01:28:41.030 --> 01:28:45.110
Das wären dann die typischen Pipeline-Stufen, und die Aufteilung,

01:28:45.650 --> 01:28:48.490
welche Operation in welcher von diesen Stufen gemacht wird.

01:28:49.950 --> 01:28:53.210
Da kann man jetzt schon, das gucken wir uns, das dauert noch ein

01:28:53.210 --> 01:28:56.050
bisschen, aber da kann man jetzt schon so ein paar erste Arten von

01:28:56.050 --> 01:28:57.490
Problemchen erkennen.

01:28:57.990 --> 01:29:02.810
Zum Beispiel, angenommen, ich habe zwei Assemblerbefehle, der eine

01:29:02.810 --> 01:29:07.230
addiert irgendwas, keine Ahnung, und schreibt es nach Register 5, und

01:29:07.230 --> 01:29:11.090
der nächste Befehl, der direkt danach kommt, liest Register 5 und will

01:29:11.090 --> 01:29:13.990
irgendwas anderes darauf, weiß ich nicht, multiplizieren, völlig egal.

01:29:13.990 --> 01:29:19.730
Also, das Problem, was man hat, ist, der Befehl, der das Ergebnis für

01:29:19.730 --> 01:29:22.850
Register 5 berechnet, wenn der in der ALU ist, dann hat er seine

01:29:22.850 --> 01:29:27.450
Addition gemacht, danach hängt der in der Memory-Stufe, der will ja

01:29:27.450 --> 01:29:30.390
gar nichts speichern, also der macht da nichts, aber der ist in dieser

01:29:30.390 --> 01:29:33.730
Stufe, und jetzt kommt ja schon der nächste Befehl, der eigentlich auf

01:29:33.730 --> 01:29:36.970
dem Ergebnis arbeiten wollte, aber das Ergebnis ist ja noch gar nicht

01:29:36.970 --> 01:29:38.430
im Register-File angekommen gewesen.

01:29:38.610 --> 01:29:42.090
Also der hat quasi veraltete Daten hier aus dem Register-File gelesen,

01:29:42.090 --> 01:29:46.190
weil erst einen Tag danach, wenn der erste Befehl in dem Writeback

01:29:46.190 --> 01:29:50.790
ist, dann wird ja erst Register 5 im Register-File aktualisiert, und

01:29:50.790 --> 01:29:52.990
da ist ja der andere Befehl schon längst fertig, das ist ja dann quasi

01:29:52.990 --> 01:29:53.650
in dem Befehl schon.

01:29:53.770 --> 01:29:56.790
Also man kann da Datenabhängigkeiten haben, die man jetzt irgendwie

01:29:56.790 --> 01:30:00.370
noch auflösen muss, das ist das eine, und man kann auch andere

01:30:00.370 --> 01:30:05.550
Abhängigkeiten haben, zum Beispiel, wenn ich hier oben ein Sprungziel

01:30:05.550 --> 01:30:09.790
berechne, dann habe ich ja quasi erst nach der Addition, sorry, nach

01:30:09.790 --> 01:30:13.890
der Adressberechnung und nach dem Vergleich, Sprung genommen oder

01:30:13.890 --> 01:30:18.590
nicht genommen, weiß ich, was der neue Program-Counterwert sein soll.

01:30:19.170 --> 01:30:22.130
Aber das heißt ja, angenommen, der neue Program-Counterwert soll sein

01:30:22.130 --> 01:30:22.930
Sprung genommen.

01:30:23.790 --> 01:30:26.190
Hier weiß ich es, oder genau genommen, ich schreibe es ja erst hier

01:30:26.190 --> 01:30:30.190
zurück, hier weiß ich es, aber dann ist hier schon ein Befehl, und

01:30:30.190 --> 01:30:31.170
hier ist auch schon ein Befehl.

01:30:31.650 --> 01:30:33.230
Das sind ja dann die falschen Befehle gewesen.

01:30:33.630 --> 01:30:37.770
Da habe ich ja quasi die nächsten Befehle aus dem Programm geholt, die

01:30:37.770 --> 01:30:40.350
hätten gar nicht ausgeführt werden sollen, weil ich hätte ja woanders

01:30:40.350 --> 01:30:41.050
hinspringen müssen.

01:30:41.470 --> 01:30:43.970
Also da muss ich dann auch gucken, wie ich das wieder aufgeräumt

01:30:43.970 --> 01:30:45.750
bekomme, und was es da für Möglichkeiten gibt.

01:30:46.130 --> 01:30:49.310
Und genau diese Pipelining nochmal am DLX, das ist so ähnlich wie

01:30:49.310 --> 01:30:52.730
MIPS, plus diese verschiedenen Arten von Konflikten, das gucken wir

01:30:52.730 --> 01:30:55.430
uns dann nächste Woche an.

01:30:55.630 --> 01:30:56.330
Bis dahin, Dankeschön.

