We beschouwen de CPU allemaal als het "brein" van een computer, maar wat betekent dat eigenlijk? Wat gebeurt er met miljarden transistors om uw computer te laten werken? In deze nieuwe vierdelige miniserie zullen we ons concentreren op het ontwerp van computerhardware dat de invoer en uitvoer omvat van wat een computer draait.

Deze serie omvat computerarchitectuur, processorcircuitontwerp, VLSI (integratie op zeer grote schaal), chipproductie en toekomstige trends in computergebruik. Als je altijd al geïnteresseerd bent geweest in details over hoe processors binnenin werken, ga dan aan de slag, want dat is wat je wilt weten om aan de slag te gaan.

We beginnen op een heel hoog niveau met wat een processor doet en hoe bouwstenen in elkaar passen in een functionerend ontwerp. Dit omvat processorkernen, geheugenhiërarchie, voorspelling van vertakkingen en meer. Ten eerste hebben we een basisdefinitie nodig van wat de CPU doet. De eenvoudigste verklaring is dat een CPU een reeks instructies volgt om op een reeks ingangen te werken. Dit kan bijvoorbeeld het lezen van een waarde uit het geheugen zijn, deze vervolgens aan een andere waarde toevoegen en het resultaat uiteindelijk op een andere locatie opslaan. Het kan ook iets complexers zijn, zoals het delen van twee getallen, als het resultaat van de vorige berekening groter is dan nul.

Als u een programma zoals een besturingssysteem of een game wilt uitvoeren, is het programma zelf een reeks instructies die de CPU moet uitvoeren. Deze instructies worden uit het geheugen en in een eenvoudige processor geladen en een voor een uitgevoerd totdat het programma is voltooid. Terwijl softwareontwikkelaars hun programma's schrijven in talen van hoog niveau, zoals C ++ of Python, kan de processor dit niet begrijpen. Het begrijpt alleen 1s en 0s, dus we hebben een manier nodig om de code in dit formaat weer te geven.




Programma's zijn gecompileerd in een reeks instructies op laag niveau. assembleertaal Als onderdeel van de Instruction Set Architecture (ISA). Dit is de set instructies die de CPU is gebouwd om te begrijpen en uit te voeren. Enkele van de meest voorkomende ISA's zijn x86, MIPS, ARM, RISC-V en PowerPC. Net zoals de syntaxis voor het schrijven van een functie in C ++ verschilt van een functie die hetzelfde doet in Python, heeft elke ISA een andere syntaxis.




Deze ISA's kunnen worden onderverdeeld in twee hoofdcategorieën: vaste lengte en variabele lengte. RISC-V ISA gebruikt instructies met een vaste lengte, wat betekent dat het bepaalt welk type commando een bepaald aantal vooraf gedefinieerde bits in elk commando is. Dit verschilt van x86 met instructies met variabele lengte. In x86 kunnen instructies op verschillende manieren en met verschillende aantallen bits voor verschillende onderdelen worden gecodeerd. Vanwege deze complexiteit is de opdrachtdecoder op x86-CPU's meestal het meest complexe deel van het hele ontwerp.

Instructies met een vaste lengte maken het decoderen gemakkelijker vanwege hun normale aard, maar beperken het totale aantal instructies dat een ISA kan ondersteunen. Gangbare versies van de RISC-V-architectuur hebben ongeveer 100 richtlijnen en hoewel het open source is, is het eigendom van x86 en weet niemand hoeveel richtlijnen er zijn. Mensen geloven over het algemeen dat er enkele duizenden x86-instructies zijn, maar het exacte aantal is niet publiekelijk beschikbaar. Ondanks de verschillen tussen ISA's, hebben ze allemaal in wezen dezelfde basisfunctionaliteit.







Nu zijn we klaar om onze computer aan te zetten en iets op te starten. De uitvoering van een commando heeft eigenlijk een paar basisonderdelen die uit vele fasen van een processor bestaan.




De eerste stap is om de instructie vanuit het geheugen naar de CPU te brengen om te starten. In de tweede stap wordt de opdracht gedecodeerd zodat de CPU kan begrijpen wat voor soort instructie het is. Er zijn veel soorten zoals rekenkundige opdrachten, vertakkingsinstructies en geheugeninstructies. Zodra de CPU leert wat voor soort instructie hij uitvoert, worden de operanden van de instructie verzameld uit het geheugen of uit interne registers in de CPU. Als u het getal A aan het B-nummer wilt toevoegen, kunt u niet optellen zonder de A- en B-waarden echt te kennen. De meeste moderne processors zijn 64 bits, wat betekent dat de grootte van elke gegevenswaarde 64 bits is.




Nadat de CPU de operanden voor de instructie heeft, gaat deze naar de uitvoeringsfase waar de verwerking bij invoer wordt uitgevoerd. Dit kan het optellen van getallen zijn, een logische manipulatie van de getallen maken of de getallen ongewijzigd doorgeven. Nadat het resultaat is berekend, moet mogelijk toegang worden verkregen tot het geheugen om het resultaat op te slaan, of de CPU houdt de waarde in een van zijn interne registers. Nadat het resultaat is opgeslagen, zal de CPU de status van de verschillende items bijwerken en doorgaan naar de volgende instructie.

Deze uitleg is natuurlijk een grote vereenvoudiging en de meeste moderne processors verdelen deze paar fasen in 20 of minder fasen om de efficiëntie te vergroten. Dit betekent dat terwijl de processor verschillende instructies in elke cyclus start en voltooit, elke instructie 20 of meer cycli nodig heeft om van begin tot eind te voltooien. Dit patroon wordt vaak een pijpleiding genoemd omdat het enige tijd kost om de pijpleiding te vullen en de vloeistof er volledig doorheen gaat, maar wanneer deze gevuld is, krijgt u een gelijkmatige output.

De hele cyclus die een instructie doorloopt, is een zeer strikt choreografisch proces, maar het kan zijn dat niet alle instructies tegelijkertijd eindigen. Het invoegen kan bijvoorbeeld erg snel zijn, terwijl het splitsen of laden uit het geheugen honderden cycli kan duren. In plaats van de hele processor te stoppen wanneer een langzame instructie is voltooid, raken de meeste moderne processors defect. Dit betekent dat ze zullen bepalen welk commando het nuttigst is om op een bepaald moment uit te voeren en andere instructies die niet gereed zijn, bufferen. Als de huidige instructie nog niet klaar is, kan de processor vooruit springen in de code om te zien of er iets anders klaar is.

Naast de ongebruikelijke uitvoering, typische moderne processors, superscalaire architectuur. Dit betekent dat de processor op elk willekeurig moment in elke fase van de pijplijn veel instructies tegelijk uitvoert. Mogelijk wachten er nog honderden om met hun executie te beginnen. Om veel instructies tegelijk uit te kunnen voeren, hebben ze meerdere exemplaren van elke pijplijnfase binnen de processors. Als een processor ziet dat de twee opdrachten klaar zijn om te worden uitgevoerd en in plaats van te wachten tot ze afzonderlijk zijn voltooid, voert hij beide tegelijkertijd uit als er geen afhankelijkheid tussen is. Een veel voorkomende toepassing hiervan wordt Simultaneous Multithreading (SMT) genoemd, ook wel Hyper-Threading genoemd. Terwijl Intel- en AMD-processors momenteel bidirectionele SMT ondersteunen, heeft IBM chips ontwikkeld die acht-weg SMT ondersteunen.

Om deze zorgvuldig gechoreografeerde uitvoering te realiseren, heeft een processor naast de basiskern veel extra elementen. Er zijn honderden afzonderlijke modules in een processor, die elk een specifiek doel dienen, maar we zullen de basis behandelen. De twee grootste en meest bruikbare zijn cache- en branch-voorspellers. Aanvullende structuren die we niet behandelen, zijn zaken als het opnieuw ordenen van buffers, record-aliastabellen en reserveringsstations.

Het doel van caches kan verwarrend zijn omdat ze vaak gegevens zoals RAM of SSD opslaan. Wat de caches onderscheidt, is hun toegangsvertraging en snelheid. Hoewel RAM extreem snel is, is het een zeer trage grootteorde voor de CPU. RAM kan honderden cycli nodig hebben om te reageren met gegevens, en de processor blijft hangen en doet niets. Als de gegevens zich niet in het RAM bevinden, kan het tienduizenden cycli duren om toegang te krijgen tot gegevens op een SSD. Zonder caches kwamen onze processors tot stilstand.

Processors hebben meestal drie cacheniveaus. geheugenhiërarchie. L1-cache is de kleinste en snelste, L2 is in het midden en L3-cache is de grootste en langzaamste. Boven de caches in de hiërarchie bevinden zich kleine records die tijdens de berekening één gegevenswaarde opslaan. Deze records zijn, in volgorde van grootte, de snelste opslagapparaten in uw systeem. Wanneer een compiler het programma op hoog niveau converteert naar de compilatietaal, bepaalt het de beste manier om deze registers te gebruiken.

Wanneer de CPU gegevens uit het geheugen opvraagt, controleert deze eerst of die gegevens zijn opgeslagen in de L1-cache. Als dat het geval is, zijn gegevens in slechts enkele cycli snel toegankelijk. Als het niet beschikbaar is, controleert de CPU L2 en zoekt vervolgens naar de L3-cache. Caches worden meestal transparant geïmplementeerd in de kernel. De kernel vraagt ​​alleen om enkele gegevens op een bepaald geheugenadres en reageert op elk niveau in zijn hiërarchie. Grootte en latentie nemen doorgaans toe in ordes van grootte naarmate we verder gaan naar de latere stadia in de geheugenhiërarchie. Als de CPU de gezochte gegevens uiteindelijk niet kan vinden in een van de caches, gaat deze pas naar het hoofdgeheugen (RAM).

In een typische processor heeft elke kern twee L1-caches: één voor gegevens en één voor instructies. L1-caches zijn in totaal ongeveer 100 kilobyte en de grootte kan variëren afhankelijk van de chip en de generatie. Elke architectuur heeft doorgaans een L2-cache, maar sommige architecturen kunnen worden gedeeld tussen twee cores. L2-caches zijn meestal enkele honderden kilobytes. Ten slotte is er een enkele L3-cache die ongeveer tientallen megabytes wordt gedeeld over alle cores.

Wanneer een processor code uitvoert, worden de instructies en gegevenswaarden die hij het meest gebruikt, in de cache opgeslagen. Dit versnelt de uitvoering aanzienlijk, omdat de processor niet constant naar het hoofdgeheugen hoeft te gaan voor de gegevens die hij nodig heeft. We zullen meer vertellen over hoe deze geheugensystemen daadwerkelijk worden geïmplementeerd in het tweede en derde deel van deze serie.

Naast caches is een van de andere belangrijke bouwstenen van een moderne processor correct branch voorspeller. Branch-instructies zijn vergelijkbaar met "if" -instructies van een processor. Als de voorwaarde waar is, wordt een reeks opdrachten uitgevoerd en als de voorwaarde onwaar is, wordt een reeks opdrachten uitgevoerd. U wilt bijvoorbeeld twee getallen vergelijken, en als ze gelijk zijn, kunt u de ene functie uitvoeren en als ze verschillen, kunt u een andere functie uitvoeren. Deze vertakkingsinstructies zijn zeer gebruikelijk en kunnen ongeveer 20% van alle instructies in een programma uitmaken.

Op het eerste gezicht lijken deze branch-instructies misschien geen probleem, maar het kan erg moeilijk zijn om een ​​processor goed te krijgen. Het is erg belangrijk om te weten, aangezien de CPU op elk moment bezig kan zijn met het uitvoeren van tien of twintig instructies tegelijk welke instructies voor het uitvoeren. Het kan 5 lussen duren om te bepalen of het huidige commando een tak is en nog eens 10 lussen om te bepalen of de voorwaarde waar is. Gedurende deze tijd is de processor mogelijk begonnen met het uitvoeren van tientallen aanvullende instructies zonder te weten of dit de juiste instructies waren.

Om dit probleem op te lossen, gebruiken alle moderne krachtige processors een techniek die speculatie wordt genoemd. Dit betekent dat de processor de branchinstructies volgt en voorspelt of de branch zal worden overgenomen. Als de gok juist is, is de processor begonnen met het uitvoeren van volgende instructies, wat een prestatiewinst oplevert. Als de gok verkeerd is, stopt de processor met de uitvoering, verwijdert hij alle verkeerde instructies die hij begon uit te voeren en begint hij vanaf het juiste punt.

Deze branchevoorspellers zijn enkele van de vroegste vormen van machinistenleren, omdat de voorspeller geleidelijk het gedrag van takken leert. Als hij te veel verkeerd raadt, zal hij het juiste gedrag beginnen te leren. Tientallen jaren van onderzoek naar branchevoorspellingstechnieken hebben geresulteerd in een nauwkeurigheid van meer dan 90% in moderne processors.

Hoewel speculatie enorme prestatieverbeteringen oplevert, legt het ook kwetsbaarheden bloot, hoewel de processor kant-en-klare instructies kan uitvoeren in plaats van te wachten op drukke instructies. De beroemde Spectre-aanval maakt gebruik van fouten in branchevoorspelling en speculatie. De aanvaller gebruikt speciaal vervaardigde code om de processor te dwingen speculatief code uit te voeren die geheugenwaarden lekt. Sommige aspecten van de speculatie moesten opnieuw worden ontworpen om ervoor te zorgen dat de gegevens niet konden worden gelekt, wat resulteerde in een lichte daling van de prestaties.

De architectuur die wordt gebruikt in moderne processors heeft de afgelopen decennia een lange weg afgelegd. Innovaties en slim design hebben geresulteerd in meer performance en beter gebruik van onderliggende hardware. CPU-fabrikanten zijn uiterst geheim over de technologieën in hun processors, dus het is onmogelijk om precies te weten wat er binnenin gebeurt. De basisprincipes van hoe computers werken, zijn echter gestandaardiseerd op alle processors. Intel kan hun verborgen saus toevoegen om de hitfrequenties van de cache te verhogen, of AMD kan een geavanceerde brancheschatter toevoegen, maar beide doen dezelfde taak.

Deze eerste blik en overzicht behandelde de meeste basisprincipes over hoe processors werken. In de volgende sectie zullen we bespreken hoe de componenten die de CPU binnenkomen, zijn ontworpen, met betrekking tot logische poorten, klok, energiebeheer, schakelschema's en meer. Blijf naar ons kijken.

Voorgestelde metingen:

Masthead-tegoed: Elektronische printplaat van Raimuda close up