Il processore Pentium
(G. Manduchi - M. Moro - 2001)
Il processore Pentium (introdotto nel 1993) rappresenta l’evoluzione della linea di processori Intel formata dalla serie 8086/88,
80286, Intel386 DX (il primo a 32 bit, introdotto nel 1985), Intel486 DX, Intel486 SX e Intel486 DX2. Rispetto alle precedenti
generazioni, il processore Pentium introduce notevoli miglioramenti: già nei primi processori della linea Pentium erano presenti: una
architettura superscalare, la predizione dinamica dei salti (dynamic branch prediction), 2 memorie cache da 8Kb ciascuna, una per i
dati e una per il codice, un bus di dati a 64 bit. I primi processori Pentium venivano costruiti con chip contenenti 3 milioni di
transistor, realizzati con tecnologia a 0.34 µm; sono utilizzati in sistemi con un clock di processore nel range 60 ÷ 200 MHz e con un
clock di bus di memoria nel range 50/60/66 MHz. Questi aspetti verranno di seguito discussi in maggiore dettaglio con riferimento ai
primi processori Pentium; alla fine di queste note si accennerà brevemente alle evoluzioni di queste caratteristiche presenti nei
successivi processori della medesima linea.
Modalità operative
Il Pentium può operare in una di 3 diverse modalità operative (modi):
• modo protetto (Protected mode)
costituisce la modalità nativa nella quale sono utilizzabili tutte le istruzioni e funzionalità del processore;
• modo reale (Real-Address Mode)
fornisce il contesto di programmazione dei predecessori a 16 bit (8086/88) con qualche estensione;
• modo di sistema (System Management Mode)
fornisce un contesto speciale per l’esecuzione di particolare codice di controllo di sistema, tipicamente in firmware, quale il
sistema di controllo automatico dell’alimentazione, in modo trasparente sia alle applicazioni che al sistema operativo
Organizzazione della Memoria
In un sistema Pentium la memoria fisica è indirizzata a byte con un indirizzo a 32 bit, con un totale quindi di 4 Gigabyte indirizzabili.
Il codice fa però riferimento ad una gestione della memoria attraverso due meccanismi: la segmentazione (sempre abilitata) e la
paginazione: ciò produce un modello di memoria, chiamato virtual memory, in cui lo spazio di indirizzamento da programma è uno
spazio logico. L’hardware di segmentazione provvede a tradurre un indirizzo logico in un indirizzo lineare (relativo ad uno spazio
‘piano’ cioè non segmentato); a valle del primo, l’hardware di paginazione, se abilitato, provvede a tradurre un indirizzo lineare in un
indirizzo fisico, altrimenti l’indirizzo lineare coincide con quello fisico.
Il modello di memoria utilizzato nell’architettura Pentium (come pure nei predecessori della famiglia Intel) è pertanto basato sul
concetto di segmento. Lo spazio di indirizzamento viene suddiviso in un certo numero di segmenti; ad ogni segmento è associato un
insieme di informazioni quali la dimensione, che è fissata dal programmatore, l’indirizzo lineare di partenza (base address) ed il
livello di protezione. All’interno di ogni segmento l’indirizzo di una locazione viene specificato da un offset a 32 bit: di conseguenza,
l’indirizzo lineare viene ottenuto sommando l’offset al base address. Un segmento può contenere codice, dati o uno stack. La
suddivisione in segmenti consente in generale una maggiore affidabilità dei programmi e del sistema (si pensi solo all’automatica
rilocabilità del codice): è comunque possibile imporre una visione ‘piana’ (ovvero lineare, non segmentata) della memoria
semplicemente associando il medesimo base address a tutti i segmenti.
Possono essere definiti fino a 16383 segmenti, ognuno ampio fino a 4 Gbyte, ottenendo quindi uno spazio di indirizzamento virtuale
di 64 Terabyte. Ogni segmento è identificato da un selettore (segment selector) a 16 bit che include un indice (13 bit) in una tabella,
contenuta in memoria centrale, che a sua volta contiene un massimo di 8192 descrittori di segmento. In ogni istante sono attive due
tabelle di descrittori, una globale (GDT, Global descriptor table), comune a tutti i programmi, e una locale (LDT, Local descriptor
table) associata al singolo programma. È compito del sistema operativo fissare la coppia di tabelle attive impostando due registri
macchina (GDTR e LDTR) che contengono i rispettivi indirizzi iniziali delle tabelle in uso, mentre il selettore include anche un bit
che precisa quale delle due tabelle deve essere utilizzata per la traduzione e altri 2 bit che specificano il livello di privilegio
nell’accesso. Un descrittore di segmento è composto da 8 byte che includono, in un particolare formato, il base address a 32 bit, la
dimensione del segmento misurata con granularità 1 byte oppure 4 Kbyte da una posizione limite da 20 bit (quindi la dimensione di
un segmento varia rispettivamente nei range 1 byte÷1 Mbyte, 4 Kbyte÷4 Gbyte), il livello di privilegio d’accesso e altre informazioni
associate al segmento.
Per motivi di efficienza del meccanismo di traduzione indirizzo logico - indirizzo lineare, durante l’esecuzione del programma
possono essere attivi (immediatamente accessibili) fino a 6 segmenti: i loro rispettivi selettori vengono caricati nella parte visibile di
altrettanti registri macchina (segment register). Il caricamento di un selettore in uno di questi registri comporta l’automatico
caricamento, in una parte non visibile del registro, del corrispondente descrittore: in questo modo il base address e la dimensione del
segmento saranno disponibili direttamente nel processore per tutti i successivi accessi a quel segmento. Il registro CS è relativo al
Code Segment, utilizzato per memorizzare il codice; il registro SS è relativo allo Stack Segment, utilizzato per memorizzare lo stack; i
registri DS, ES, FS, GS sono relativi a 4 segmenti dati, in particolare il registro DS è relativo al Data Segment, il registro ES è
relativo allo String Segment, utilizzato per contenere informazioni quali le stringhe alfanumeriche.
I registri di segmento e conseguentemente i segmenti utilizzati nell’indirizzamento delle istruzioni possono essere impliciti
(dipendere cioè dal tipo di istruzione: per esempio istruzioni di manipolazione dello stack utilizzano implicitamente SS) oppure
essere esplicitamente indicati nell’istruzione. A tal scopo l’architettura Pentium consente una notevole flessibilità nell’indirizzamento
in memoria riservando a ciò un byte nel codice di istruzione.
La forma più generale di indirizzamento coinvolge un segment register, un base register, un index register, uno scale factor (1,2,4,8)
ed un displacement fornito nell’istruzione. A parte il base address del segmento, il resto costituisce il calcolo dell’indirizzo effettivo
A partire da queste informazioni, l’indirizzo lineare viene calcolato nell’apposito stadio del ciclo di esecuzione come:
segment base address + base register + (index register * scale factor) + displacement
1
Come si vede, l’indirizzamento è estremamente flessibile e può essere utilizzato dai compilatori per un’efficiente organizzazione del
codice che accede ad array uni o multidimensionali e a strutture di dati complesse, evitando di inserire istruzioni per il calcolo
esplicito degli indirizzi.
Tipi di dati
Il processore Pentium gestisce, come tipi base, parole a 8 (byte), 16 (word), 32 (doubleword) e 64 bit (quadword). L’ordinamento dei
byte in memoria è di tipo little endian (la parte meno significativa ad indirizzo più basso). Istruzioni specializzate danno di questi tipi
ulteriori interpretazioni quali naturali, interi (con segno), interi BCD, puntatori near (indirizzo effettivo a 32 bit come offset),
puntatori far (indirizzo logico a 48 bit, 16 per il selettore di segmento e 32 per l’offset), sequenze di bit (bit field, fino a 32 bit, e bit
string, fino a 232-1 bit), stringhe (sequenze di byte, word o doubleword fino a 4 Gigabyte), floating point in precisione singola (32
bit), doppia (64 bit), estesa (80 bit).
Organizzazione dei registri
Oltre ai 6 registri di segmento, l’architettura Pentium definisce i seguenti registri general-purpose a 32 bit: EAX, EBX, ECX, EDX,
EBP, ESI, EDI, ESP, che possono essere anche utilizzati come base register e index register per l'indirizzamento.
Pur essendo in generale interscambiabili, alcuni di questi registri assumono un ruolo particolare in alcune istruzioni. Per esempio, il
registro ESP viene utilizzato come Stack Pointer (ovvero contiene l’offset della testa dello stack nello Stack Segment), il registro EBP
è utilizzato come frame pointer, i registri EDI ed ESI sono utilizzati come base register nel Data Segment e nello String Segment,
rispettivamente.
L’indirizzo dell’istruzione successiva durante l’esecuzione del programma viene implicitamente definito dal registro EIP, che
contiene l’offset dell’istruzione successiva nel Code segment.
I registri a 32 bit EAX, EBX, ECX, EDX possono essere anche visti come 4 registri a 16 bit (la parte meno significativa di ciascuno),
col nome di AX, BX, CX, DX, oppure come 8 registri ad 8 bit, col nome di: AL, AH, BL, BH, CL, CH, DL, DH ove, ad esempio,
AL ed AH indicano rispettivamente il byte meno significativo ed il byte più significativo del registro a 16 bit AX.
Il registro EFLAGS a 32 bit contiene infine informazioni sullo stato del processore, compresi gli usuali bit di condizione.
Pipelining
Similmente al processore Intel486, il ciclo di esecuzione delle istruzioni che non coinvolgono l’unità floating point è suddiviso nei
seguenti 5 stadi:
PF
D1
D2
EX
WB
prefetch
instruction decode
address generate
execute - ALU ed accesso cache
write back
Il primo stadio di esecuzione è chiamato prefetch perché consiste nella lettura della codifica dell’istruzione successiva alla corrente.
Nel secondo stadio (Decode1) avviene la decodifica dell’istruzione, mentre nel successivo stadio (Decode2) vengono calcolati gli
indirizzi effettivi degli operandi. Nello stadio di Execute avviene sia l’accesso agli operandi dalla cache di dati che l’effettiva
esecuzione dell’istruzione. Conseguentemente, se l’istruzione obbliga ad un accesso di memoria, l’esecuzione di questo stadio
richiederà più di un ciclo di clock. Nello stadio di Writeback le istruzioni sono infine abilitate a modificare lo stato del processore
tramite la scrittura dei risultati nei registri o in memoria.
L’architettura superscalare è basata sulla multiplazione delle unità funzionali che soprassiedono al ciclo di esecuzione, ottenendo in
questo modo la possibilità di eseguire in parallelo stadi omologhi di istruzioni successive. Pertanto, mentre nel processore Intel486
solo una istruzione poteva essere in corso ad ogni stadio, ottenendo quindi la seguente organizzazione nell’esecuzione della sequenza
di istruzioni I1 I2 I3 I4,
PF
D1
D2
EX
WB
I1
I2
I1
I3
I2
I1
I4
I3
I2
I1
I4
I3
I2
I1
I4
I3
I2
I4
I3
I4
l’architettura superscalare del processore Pentium consente la contemporanea esecuzione di due istruzioni in parallelo per ogni stadio
del pipeline. È possibile allora, per una sequenza di istruzioni I1 - I8, la seguente organizzazione di esecuzione:
PF
D1
D2
EX
I1
I2
I3
I4
I1
I2
I5
I6
I3
I4
I1
I2
I7
I8
I5
I6
I3
I4
I1
I2
I7
I8
I5
I6
I3
I4
I7
I8
I5
I6
I7
I8
2
WB
I1
I2
I3
I4
I5
I6
I7
I8
Poiché ogni stadio del pipeline richiede in genere un periodo di clock per la sua esecuzione, è possibile, in condizione ideale, arrivare
ad una frequenza di esecuzione delle istruzioni pari al doppio della frequenza di clock. Tuttavia diversi fattori limitano questa
frequenza teorica: solo un sottoinsieme del set di istruzioni compatibili può essere eseguito in parallelo, ed inoltre non devono
esistere dipendenze tra la coppia di istruzioni eseguita concorrentemente, come avviene, per esempio, nel caso in cui i risultati della
prima istruzione rappresentino l’ingresso della seconda istruzione. In più, istruzioni di salto possono essere eseguite
concorrentemente solo se rappresentano la seconda istruzione della coppia (di seguito verranno illustrate le conseguenze delle
istruzioni di salto nell’esecuzione del pipeline).
La presenza di una coppia di istruzioni incompatibile viene rilevata durante lo stadio D1 dall’hardware, che provvede a disattivare la
seconda unità durante gli stadi di esecuzione della prima istruzione. Conseguentemente, la seconda istruzione non verrà eseguita in
parallelo, ma seguirà la prima nel pipeline con uno stadio di ritardo.
Floating point pipeline
L’unità floating point è integrata nello stesso chip del processore Pentium e consente l’esecuzione di un’istruzione floating point ad
ogni ciclo di clock. È inoltre supportato un limitato parallelismo che consente l’esecuzione concorrente di un’istruzione floating point
e un’istruzione di scambio. La motivazione di ciò è data dal fatto che i registri floating point sono organizzati in una struttura a stack,
e che le operazioni floating point normalmente coinvolgono la coppia di registri in testa allo stack. Ecco che di frequente avviene che
i contenuti dei registri in testa allo stack debbano essere scambiati fra loro durante l’esecuzione del programma. La scelta dei
progettisti è quindi una conseguenza della legge di Amdahl che asserisce che l’incremento della performance di un computer,
ottenuto dal miglioramento di qualche suo componente, è limitato dalla frazione di tempo in cui l’esecuzione del programma utilizza
quel componente.
L’unità floating point è organizzata in un pipeline con 8 stadi, i cui primi 5 coincidono con gli stadi eseguiti dalle istruzioni intere.
Istruzioni floating point non possono essere eseguite contemporaneamente (nello stesso stadio) ad istruzioni intere poiché utilizzano
per quei 5 stadi le medesime unità funzionali.
Risoluzione dei Pipeline Hazard
Come è noto, la reale esecuzione di una sequenza di istruzioni comporta una serie di condizioni che prevengono l’utilizzo ottimale
del pipeline. Tali condizioni possono essere divise in due principali categorie: data hazard e branch hazard.
Il tipo di data hazard più comune è denominato RAW (read-after-write) ed avviene quando l’istruzione deve utilizzare i risultati
dell’istruzione precedente. Esso può avvenire anche nel pipeline del Pentium, poiché la lettura degli operandi viene eseguita nello
stadio EX, che avviene contemporaneamente allo stadio WB (scrittura operandi) dell’istruzione precedente. In tal caso l’hazard è
identificato dall’hardware, che provvedere a sospendere (stall) lo stadio di EX fino a che lo stadio WB dell’istruzione precedente non
è terminato.
Il RAW data hazard può anche avvenire nel pipeline dell’unità floating point, ma in tal caso è adottata nel processore Pentium una
tecnica, chiamata bypass, che consente di mettere a disposizione l’informazione richiesta ad uno stadio appena possibile, anche se
non ancora riportata in memoria o nei registri. In questa maniera l’informazione può essere utilizzata nel periodo di clock successivo
allo stesso stadio dall’istruzione successiva, mentre contemporaneamente viene riportata nella memoria o nei registri.
Il branch hazard avviene quando un’istruzione di salto modifica il flusso di esecuzione di un programma, forzando di conseguenza lo
‘scaricamento’ (flush) degli stadi del pipeline precedenti a quello in cui l’indirizzo effettivo di salto viene calcolato, che sono relativi
ad istruzioni che sono state iniziate ma che in realtà non dovevano essere eseguite e perciò non devono essere completate.
L’architettura del Pentium adotta inoltre una tecnica di predizione (branch prediction) che ‘tira ad indovinare’ l’indirizzo della
successiva istruzione già nella prima fase di prefetch (PF): l’hardware determina in questa fase se l’istruzione è di salto, ed usa una
tabella chiamata Branch Prediction Buffer (BTB), composta di un insieme di coppie di indirizzi. Utilizzando una tecnica molto simile
al’accesso nella memoria cache, viene determinato se è presente nella BTB una coppia di indirizzi il cui primo indirizzo corrisponde
all’indirizzo corrente dell’istruzione di salto. Se tale coppia viene trovata (hit), il secondo indirizzo viene assunto come l’indirizzo
della successiva istruzione e caricato nel program counter per la successiva fase di prefetch. Se tale indirizzo viene previsto
correttamente, l’esecuzione di un’istruzione di salto non comporta alcuna penalità nell’esecuzione del pipeline. Nella fase EX,
quando viene calcolato l’indirizzo effettivo di salto, viene nuovamente fatto accesso alla BTB per determinare se la previsione fatta
risulti corretta. In caso contrario, l’indirizzo effettivo di salto viene memorizzato nella BTB assieme all’indirizzo dell’istruzione di
salto, e viene eseguito il flush del pipeline, annullando quindi la parziale esecuzione delle tre istruzioni successive e incorrendo in un
ritardo di tre periodi di clock. La motivazione alla base di tale tecnica è data dall’osservazione che nella grande maggioranza dei casi
le istruzioni di salto vengono prodotte dai compilatori in corrispondenza a loop nel programma. È pertanto assai probabile che la
seconda volta che la stessa istruzione di salto viene eseguita, essa comporterà lo stesso effetto della volta precedente.
Una differente tecnica, adottata in altri processori, ma non nel Pentium, è data dall’utilizzo di delayed branch, ovvero di istruzioni di
salto il cui effetto si ha solo un certo numero (uno, due o tre) di istruzioni successive. Ciò consente di evitare il flush del pipeline in
caso di salto, ma richiede un’accurata organizzazione delle istruzioni da parte del compilatore: operazione non sempre applicabile e
che comporta conseguentemente il frequente inserimento di istruzioni NOP (dummy) dopo un’istruzione di salto. Utilizzando invece
la tecnica di branch prediction, tale inconveniente viene risolto, senza per altro richiedere alcuna manipolazione del codice.
Va tuttavia osservato che, anche nel Pentium, la qualità di un programma dal punto di vista della velocità di esecuzione, dipende
dalla capacità del compilatore di generare un codice che sfrutti al meglio l’architettura del pipeline (evitando per esempio conflitti
RAW) e l’architettura superscalare (rendendo contigue istruzioni compatibili).
Architettura della Cache
Due memorie cache da 8Kbyte ciascuna sono integrate nel processore Pentium, una per le istruzioni (Code cache) e una per i dati
(Data cache). L’utilizzo di due cache diverse evita i conflitti di accesso che avverrebbero altrimenti quando, per esempio, lo stadio
PF legge un’istruzione dalla memoria e lo stadio WB scrive i risultati dell’istruzione in memoria. Sempre per evitare conflitti di
3
accesso da parte dei vari stadi del pipeline, la cache ammette la ricerca contemporanea di due indirizzi tramite due porte indipendenti.
Ciò consente, per esempio, che l’accesso alla memoria da parte dello stadio EX non vada in conflitto con la scrittura da parte dello
stadio WB.
Organizzazione
Code Cache e Data Cache sono organizzate come memorie set-associative a 2 vie. Ogni cache definisce 128 set ed ogni set contiene
2 blocchi (linee), ognuno con il proprio tag. Poiché ogni blocco della cache è composto di 32 byte, in un indirizzo fisico a 32 bit i 5
bit meno significativi sono utilizzati come offset all’interno del blocco, un campo intermedio di 7 bit individua il set di appartenenza,
e il tag è costituito dai 20 bit più significativi.
Ad ogni set, composto da due blocchi, è associato un bit per la determinazione del blocco da rendere invalido quando un nuovo
blocco per quel set (univocamente determinato dall’indirizzo) debba essere caricato dalla memoria. La politica utilizzata per il
rimpiazzo è del tipo Least Recently Used (LRU) che definisce il blocco da rimpiazzare quella utilizzato meno di recente.
Per quanto riguarda le operazioni di scrittura, è possibile definire, tramite un bit in un control register, sia la politica di write back (il
blocco viene scritto in memoria centrale solo quando è scaricato) che di write through (la memoria viene riscritta ad ogni accesso in
scrittura della cache).
Per supportare architetture multiprocessore, in cui ogni processore dispone della propria cache contenente una copia di parte della
memoria (unica) centrale, la data cache supporta il protocollo write-back MESI (modified/exclusive/shared/invalid). Ad ogni blocco
viene associato uno dei seguenti 4 stati:
• M - Modified: indica che il blocco è presente in una sola cache del sistema, ed il suo contenuto è diverso da quello della memoria
centrale (a causa di un’operazione di scrittura);
• E - Exclusive: indica che il blocco è presente in una sola cache del sistema, ed il suo contenuto è uguale a quello della memoria
centrale;
• S - Shared: indica che il blocco è potenzialmente condivisa da altre cache del sistema. Una lettura di un blocco nello stato S non
genera attività sul bus di comunicazione, ma un’operazione di scrittura inizierà un ciclo di write through e invaliderà le altre
copie dello stesso blocco nelle altre cache;
• I - Invalid: indica che il blocco non è valido e un suo accesso causerà un miss, con la conseguente rilettura del blocco dalla
memoria centrale.
L’architettura della cache supporta pertanto anche la possibilità che un’operazione di scrittura in un blocco Shared inizi un ciclo di
snooping sul bus di comunicazione, in cui le copie eventualmente presenti nelle altre cache vengono invalidate. Tale operazione non
interferisce con le normali operazioni di accesso locale poiché una terza porta indipendente della cache (oltre alle due citate prima) è
riservata esclusivamente alle operazioni di snooping.
Organizzazione della paginazione
Tutti gli indirizzi gestiti dalla cache sono indirizzi fisici, ovvero corrispondono alle effettive locazioni di memoria dei byte indirizzati.
Per supportare sistemi paginati, l’architettura Pentium definisce un Memory Management Unit (MMU) per la traduzione di indirizzi
lineari in indirizzi fisici che, se abilitata, precede l’accesso alla cache.
La memoria viene normalmente suddivisa in pagine di 4 Kbyte: le pagine fisiche sono allineate su frontiera 4 Kbyte. Di conseguenza,
i 12 bit meno significativi dell’indirizzo lineare contengono l’offset all’interno della pagina. Sono definiti due livelli di tabella per
l’organizzazione delle tabelle delle pagine. I 10 bit più significativi (bit 22÷31) dell’indirizzo identificano un entry nel Page
directory, il cui indirizzo iniziale è caricato in un registro macchina (PDBR); il Page directory è rappresentato da una pagina
contenente un massimo di 1024 entry a 32 bit. Ogni entry identifica a sua volta una Page table, contenendo, oltre ad altri bit di
controllo, i 20 bit più significativi dell'indirizzo iniziale della Page table. All’interno di questa i bit 12÷21 dell’indirizzo lineare
identificano un Page Table Entry contenente, oltre ad altri bit di controllo, i 20 bit più significativi dell’indirizzo fisico iniziale della
pagina di memoria corrispondente: a questi viene giustapposto l’offset prelevato dall’indirizzo lineare per ottenere l’indirizzo fisico
della locazione cui far accesso. Il sistema operativo può stabilire se associare un unico Page directory a tutti i task in esecuzione
oppure riservarne uno per ciascun task o adottare una situazione intermedia tra le due.
Una speciale eccezione viene sollevata nel caso che l’entry nel Page table (o nel Page directory) indirizzato segnali, con l’apposito
bit di controllo, che la pagina non è presente in memoria: in questo caso gli altri bit dell’entry possono essere utilizzati dal sistema
operativo, attivato dall’eccezione sollevata, per indicare dove prelevare, ad esempio in memoria secondaria. la pagina da caricare in
memoria centrale. La gestione dell’eccezione si completa con la riesecuzione dell’istruzione che non si era potuta completare per
mancanza della pagina.
Poiché Page directory e Page table risiedono tutti in memoria, una traduzione da indirizzo lineare ad indirizzo fisico, necessaria ogni
volta che si debba eseguire un accesso alla memoria, richiederebbe due accessi aggiuntivi alla memoria. Per ovviare la maggior parte
delle volte a tale inconveniente, associato a ciascuna cache vi è un Table Lookaside Buffer (TLB) che consente la rapida
trasformazione dell’indirizzo lineare. Il TLB ha un’organizzazione molto simile alla cache, e definisce un certo numero di coppie di
tag (20 bit) ed indirizzo (32 bit). Tale struttura consente di ricavare l’indirizzo fisico della pagina corrispondente ai 20 bit più
significativi dell’indirizzo lineare eseguendo una ricerca nel TLB. Se viene identificata una coppia il cui tag corrisponde ai 20 bit
cercati, l’indirizzo fisico associato indica l’indirizzo iniziale della pagina corrispondente. Meccanismi del tutto analoghi a quelli
utilizzati nella cache provvedono al rimpiazzo delle coppie tag-indirizzo quando il TLB risulti pieno ed una nuova traduzione venga
richiesta. In base al principio di località, che asserisce che nella maggior parte dei casi l’accesso in memoria tende ad essere
localizzato, il meccanismo di traduzione rapido offerto dal TLB risulta molto efficiente, poiché nella stragrande maggioranza dei casi
l’accesso alla memoria causerà un hit nel TLB, e non richiederà cicli di clock aggiuntivi per l’accesso in memoria delle tabelle delle
pagine.
Bus di Comunicazione
Il bus di comunicazione del Pentium è caratterizzato da 64 linee di dati. È pertanto possibile trasferire fino ad 8 byte in un singolo
ciclo di lettura/scrittura. L’indirizzamento è a 32 bit, consentendo quindi, come già detto, uno spazio di indirizzamento fisico di 4
4
Gbyte. Una conseguenza della dimensione del data bus è che per l’indirizzamento vengono utilizzate 29 linee, corrispondenti ai bit
A31-A3 di indirizzo. L’indirizzamento del singolo byte, concettualmente specificato anche dai bit A2-A0 di indirizzo, viene ottenuto
tramite 8 linee (B0-B7) che specificano il sottoinsieme (contiguo) degli 8 byte trasferito nel ciclo di lettura/scrittura (similmente alle
linee LDS e UDS nel 68000 per un data bus a 16 bit).
Il bus di comunicazione supporta le seguenti operazioni:
- Single transfer cycle: trasferimento di un singolo dato (byte, word, double word, quadword). Se la memoria, e più in generale la
periferica indirizzata, è sufficientemente veloce, l’intero ciclo di trasferimento richiede due periodi di clock: se consideriamo un ciclo
di lettura ad esempio, al primo periodo di clock il processore attiva le linee di indirizzo A31-A3 e le linee di selezione byte B0-B7, e
segnala la validità dell’indirizzo tramite la linea di controllo ADS# (# significa in logica negata). La disponibilità del dato è segnalata
dalla periferica tramite la linea di controllo BRDY#, che viene acquisita dal processore al successivo periodo di clock, assieme al
dato. Se la periferica non è sufficientemente veloce, il processore inserisce dei cicli di attesa (wait cycle) fino a che la linea BRDY#
indicherà la disponibilità del dato.
- Burst Transfer: questo trasferimento è utilizzato quando si debba trasferire una sequenza di dati ad indirizzi consecutivi. Dopo il
primo ciclo di trasferimento, in cui viene presentato l’indirizzo iniziale del blocco di dati da trasferire, nei successivi cicli l’indirizzo
(consecutivo) dei dati viene calcolato dall’hardware esterno che pertanto mette direttamente a disposizione il dato, segnalato dalla
linea BRDY#. È quindi possibile ottenere una frequenza di trasferimento pari a 8 byte per ogni periodo di clock, nel caso il
dispositivo esterno sia sufficientemente veloce, e quindi il processore non debba inserire cicli di attesa.
- Locked Transfer: tramite la linea di controllo LOCK# è possibile definire una sequenza atomica di operazioni sul bus. Ciò consente,
ad esempio, la modifica di locazioni di memoria tramite una lettura seguita da una scrittura con la garanzia che nel frattempo il bus
non venga utilizzato da qualche altro processore, possibilmente modificando la locazione di memoria.
- Bus Ownership Management: tramite le linee di controllo HOLD e HLDA è possibile la richiesta da parte di un processore di
diventare il proprietario del bus (bus holder) e quindi di supervisionare l’attività del bus. Questo tipo di protocollo è utilizzato sia in
un sistema multiprocessore, che in un sistema a singolo processore quando, ad esempio, un DMA controller esterno richiede il bus
per eseguire un trasferimento da/per la memoria centrale.
- Interrupt Acknowledge: questo ciclo viene eseguito in risposta ad un interrupt, per ottenere dalla periferica l’interrupt vector
(compreso tra 0 e 255) richiesto per identificare la routine di interruzione associata.
- Inquire Cycle: questo ciclo viene eseguito in conseguenza ad un accesso in scrittura ad una linea dalla cache il cui stato è S
(Shared), per invalidare eventuali altre copie della stessa porzione di memoria contenute nelle cache degli altri processori del sistema.
Il ciclo viene eseguito dopo aver ricopiato il contenuto della linea in memoria centrale, e coinvolge le linee del bus A31-A5,
necessarie per l’identificazione della linea (blocco di 32 byte) nelle cache.
- I/O cycles: cicli di Ingresso/Uscita sono equivalenti ai normali cicli di trasferimento dati (sia burst che non) salvo che la linea di
controllo M/IO# del bus indica all’hardware esterno che è in corso un’operazione di I/O. Lo spazio di indirizzamento a disposizione
delle operazioni di I/O è di 64 Kbyte, ed il set di istruzioni del Pentium definisce esplicitamente istruzioni di Ingresso/Uscita che
trasferiscono il contenuto di un registro interno da e per i registri esterni dei dispositivi di I/O (I/O port). È possibile definire anche
un’architettura di I/O memory mapped, non utilizzando quindi la linea M/IO#. In questo caso le periferiche vengono viste come
normali locazioni di memoria, e per la programmazione vengono utilizzate le normali istruzioni di trasferimento dati.
Evoluzioni recenti
La linea Pentium, introdotta nel 1993, è andata costantemente evolvendosi (con i processori della famiglia P6: Pentium Pro, Pentium
MMX, Pentium II, ...), fino agli attuali processori Pentium III e agli imminenti Pentium 4, che consentono prestazioni estremamente
elevate. Di seguito si elencheranno brevemente le nuove tecnologie adottate.
P6 Microarchitecture Dynamic Execution Technology
Definisce nuove tecniche di branch prediction per la predizione del nuovo indirizzo anche attraverso branch multipli, consentendo
quindi la riduzione delle operazioni di flush del pipeline, e garantendo quindi una maggiore velocità di esecuzione. La sequenza delle
istruzioni viene inoltre opportunamente riordinata (scheduled) al fine di utilizzare al meglio il parallelismo insito nell’architettura
superscalare e di ridurre i data hazard nel pipeline. Si osservi che questo riordino, a differenza del riordino delle istruzioni eseguito
dallo stadio di ottimizzazione del compilatore, viene eseguito dall’hardware run-time, garantendo tuttavia la correttezza del
programma.
Pentium PRO (1996)
Si tratta di un processore con architettura interna di tipo RISC (istruzioni CISC x86 ricondotte a microistruzioni RISC86 (MicroOps)). Il pipeline è a 14 stadi e il fattore superscalare è 3 (3 pipeline). Vi sono inoltre 2 cache di primo livello (L1) da 8 Kbyte (una
per le istruzioni, una per i dati) e una di secondo livello (L2) interna (integrata nel medesimo chip del processore) da 512/1024
Kbyte. Altre caratteristiche: include 5.5 milioni di transistor con tecnologia 0.35 µm, viene usato in sistemi con clock di processore
nel range 150÷200 MHz e clock di bus di memoria da 66 MHz, con possibilità di multiprocessing (2 o 4 processori).
Pentium MMX
Introdotto nel 1997, utilizza la tecnologia MMX che comprende un nuovo set di 57 istruzioni per la rapida elaborazione di grosse
quantità di dati. Questa tecnologia è principalmente orientata all’aumento delle prestazioni di applicazioni grafiche, che normalmente
richiedono la manipolazione di grandi flussi di dati. La linea MMX introduce anche la tecnologia SIMD (Single Instruction, Multiple
Data) consentendo la manipolazione di interi vettori di dati nella singola istruzione. È inoltre definito un set esteso di registri che
introduce 8 nuovi registri a 64 bit. Altre caratteristiche: include 4.5 milioni di transistor con tecnologia 0.35 µm, viene usato in
sistemi con clock di processore nel range 166÷266 MHz e clock di bus di memoria da 66 MHz.
Pentium II e nuovo Layout (1997)
Il processore Pentium II non consiste di un singolo chip, ma è composto di una cartuccia (SEC cartridge packaging) contenente più
chip collegati fra loro da bus dedicati. Ciò consente l’utilizzo di cache L1 da 32 Kbyte (due da 16) e cache L2 di 512 Kbyte,
5
quest’ultima collegata al processore tramite un bus indipendente dal bus di sistema (Dual Independent Bus architecture),
consentendo quindi una più elevata banda di comunicazione. La cache di secondo livello supporta inoltre l’utilizzo di codici ECC
(Error Correction Code) per minimizzare la probabilità di errori di comunicazione.
La frequenza di clock supportata dalla linea Pentium II è compresa tra 233 MHz e 450 MHz; quella del bus di memoria
è di 66 o 100 MHz. Tali prestazioni sono ottenute riducendo a 0.25 micron lo spessore dei componenti integrati nel
chip, cosa che ha consentito di ottenere chip con 7.5 milioni di transistor.
Pentium III
L’organizzazione interna è simile al Pentium II, inizialmente (versione Katmai) con cache L2 esterna da 512 Kbyte, successivamente
(versione Coppermine) con cache L2 interna, e quindi più veloce, da 256 Kbyte. Rispetto al Pentium II ha in più:
un set di 70 istruzioni (SSE – Streaming SIMD Extensions) (SIMD = Single Instruction Multiple Data) in più pensate per
rendere più veloci le elaborazioni multimediali;
frequenza di clock tra 500 MHz e 1 GHz, bus di memoria a 100 MHz – 133 MHz;
tecnologia 0.25 – 0.18 µm: chip con quasi 28 milioni di transistor.
Pentium 4
Introdotto alla fine del 2000, presenta una organizzazione interna innovata rispetto alla P6 (indicata con il termine Netburst)
caratterizzata da una pipeline di 20 stadi (hyper-pipelined), da 2 ALU che operano a una frequenza doppia rispetto al clock
principale, da un bus interno da 256 bit con cui è collegata la cache L2 interna da 256 (512) Kbyte. La cache L1 per le istruzioni è in
grado di contenere 12000 Micro-Ops, quella per i dati è da 8 Kbyte. Presenta inoltre:
un set di 144 nuove istruzioni (SSE 2) per rendere più veloci le elaborazioni multimediali;
una tecnica di parallelismo interno denominata hypertbreading attraverso la quale un processore fisico viene visto come una
coppia (o più) di processori logici che hanno ciascuno una propria istanza dello stato dell’architettura epperò condividono un
unico set di risorse fisiche di elaborazione;
frequenza di clock a partire da 1.5 GHz, bus di memoria a 400 (533/800) MHz;
tecnologia 0.18 (0.13) µm: chip con 42 (55) milioni di transistor.
6