di  -  mercoledì 24 febbraio 2010

L’avvento degli anni ’90 sembra dare una scossa concreta al settore dei microprocessori CISC, che con l’introduzione e la diffusione delle architetture di tipo RISC hanno subito un duro colpo relativamente a prestazioni, costi e consumi.

Apre le danze Intel nell’89 col suo 80486 (di cui parleremo in qualche futuro articolo), realizzando una CPU in grado di erogare ben 15 MIPS (e 1 MFLOPS) a 25Mhz: numeri da fantascienza se consideriamo la sua complicata ISA, che si porta dietro la famigerata e mai abbastanza indigesta retrocompatibilità x86.

Motorola, che è solita marcare stretta la grande rivale, si presenta l’anno successivo con un gioiello tecnologico che riesce a fare molto meglio: 20 MIPS (e 3,5 MFLOPS) a 25Mhz. Quasi un’istruzione per ciclo di clock per un 68040 che conta 1,17 milioni di transistor: più di 4 volte quelli del suo predecessore, il 68030 (di cui abbiamo parlato nel precedente articolo).

Numeri da RISC, insomma, anche se questi ultimi continuano ad avere la meglio sui consumi. Ma il tabù della velocità appannaggio dei RISC comincia a vacillare, e il segnale è forte: i CISC posso finalmente dire la loro. Infatti uno dei più quotati RISC dell’epoca, lo SPARC v7, alla stessa frequenza veniva dato per 16 MIPS (e 2,6 MFLOPS), e costava più del doppio.

Ovviamente MIPS e MFLOPS di per sé non vogliono dire nulla, visto che si tratta di architetture completamente diverse e le prestazioni è meglio valutarle sul campo, con le applicazioni reali. Al solito, si tratta di numeri buoni per il marketing, ma certamente fa impressione vedere cifre così elevate accostate a dei CISC, specialmente in un’epoca in cui i RISC davano battaglia e infliggevano umiliazioni a colpi di MIPS (per non parlare dei MFLOPS).

Tutto ciò è frutto del sapiente lavoro di Motorola, che con questa CPU ha introdotto un’architettura completamente Harvard con la cache istruzioni e dati completamente indipendenti (avendo ognuna un’MMU associata), una pipeline intera a 6 stadi e una in virgola mobile a 3:

Mentre col 68030 la singola MMU doveva occuparsi della traduzione di tutti gli indirizzi, le due presenti adesso provvedono separatamente e in maniera del tutto indipendente e parallela all’operazione per gli indirizzi delle istruzioni e dei dati.

Infatti ognuna presenta due master page table (una per le pagine della modalità utente, e una per quella supervisore), registri di controllo, una coppia di registri per la traduzione trasparente di due aree di memoria e 64 elementi nell’Address Translation Cache (ATC) che memorizzano gli ultimi 4 indirizzi virtuali tradotti.

In realtà proprio sulle MMU si registrano le maggiori differenze rispetto al suo predecessore, e non si tratta di scelte positive. Mentre in precedenza era possibile utilizzare la stessa master page table per la modalità utente e quella supervisore, oppure usarne due diverse, adesso quest’ultima è l’unica via.

Viene meno anche l’enorme flessibilità garantita dalla possibilità di poter definire arbitrariamente da 1 a 4 livelli per gli alberi di traduzione, e pagine di dimensione variabile da 256 byte a 32KB. Col 68040 ci sono solo 3 livelli fissi e le pagine possono essere soltanto di 4 o 8KB; sparisce anche qualche caratteristica minore, come pure alcune istruzioni (ne sono rimaste soltanto due: PTEST e PFLUSH).

Fortunatamente permane la possibilità di utilizzare un meccanismo di indirezione (di cui non avevo parlato in precedenza; ne approfitto adesso per farlo) per i descrittori di pagina, oppure per le page table, che ne consente la condivisione fra più task (o processi che dir si voglia):

Molto utile per risparmiare memoria e facilitare la condivisione di codice e/o dati.

Sono, inoltre, presenti due bit che implementano la Physical Address Extension (PAE; sigla che ricorda un’estensione analoga che Intel introdurrà soltanto coi PentiumPro) e che il processore riporta sul bus esterno assieme alle linee d’indirizzo; quindi possono servire per estendere lo spazio d’indirizzamento della memoria oltre i canonici 4GB (fino a 16GB, per la precisione) o per altri usi. Anno: 1990, ed è bene rimarcarlo.

I tagli non sorprendono e non devono deludere: si tratta sicuramente di un compromesso necessario per semplificare le due MMU senza aumentare troppo i costi e la complessità del chip (che per l’epoca era un autentico mostro). D’altra parte bisogna ricordare che la gestione di queste unità generalmente è a carico del sistema operativo e non delle applicazioni, per cui questi cambiamenti non sono direttamente percepibili.

Sempre rispetto al 68030, le cache sono state portate ciascuna da 256 byte a 4KB, per un totale di 8KB. E’ cambiata la loro organizzazione (associativa a 4 vie), in quanto adesso sono suddivise in 64 insiemi da 4 linee ciascuna, con ogni linea che, come in precedenza, contiene fino a 4 longword.

Una così elevata quantità di cache consente di ridurre sensibilmente l’accesso al bus, ottimizzando l’uso della banda di memoria e riducendo al contempo la latenza per la lettura dei dati. Per quanto riguarda la scrittura, è presente la modalità write-through, ma rispetto al passato è disponibile anche la nuova copyback, la cui implementazione ha richiesto una modifica nel formato delle linee della sola cache dati:

Il concetto è che le operazioni di scrittura non comportano automaticamente la memorizzazione dei dati, oltre che nella cache, anche nella memoria esterna, come avviene usualmente. I dati scritti in cache, ma non ancora copiati in memoria, vengono marcati con un bit chiamato dirty.

La scrittura avviene soltanto a seguito di particolari condizioni, come ad esempio per la sostituzione della linea di cache in cui risiedono (perché è diventata “vecchia” e dev’essere rimpiazzata con una nuova), oppure in maniera programmatica (tramite l’uso di apposite istruzioni che invalidano la linea). In questo modo c’è un notevole risparmio di banda di memoria, perché gli accessi vengono accorpati e, ove possibile, sfruttato il veloce burst mode per i trasferimenti (in precedenza era utilizzabile soltanto in lettura).

Burst mode che trova la sua incarnazione nella nuova (e unica) istruzione MOVE16 che, come suggerisce il nome, è in grado di spostare (copiare, in realtà) 16 byte alla volta, ovviamente a indirizzi multipli di 16 (cioè 4 longword, o una linea di cache).

Ma a fare la parte del leone è, manco a dirlo, la nuova pipeline a 6 stadi che rappresenta un grosso passo in avanti rispetto a quella a 3 stadi del 68030, in quanto il lavoro complessivo necessario per portare a termine l’esecuzione di un’istruzione consente una maggior parallelizzazione delle operazioni interne, e una più efficace loro sovrapposizione.

Per essere più chiari, questo significa che il calcolo dell’indirizzo dell’operando da prelevare dalla memoria non rappresenta più un collo di bottiglia, come nel suo predecessore, in quanto adesso viene effettuato in un apposito stadio. Quindi in quello successivo, che si occupa di recuperare effettivamente il dato, l’indirizzo è già disponibile e il controller della memoria può verificare se è presente in cache oppure recuperarlo dalla memoria.

Da notare la presenza di due stadi “nascosti”, denominati shadow, che consentono di eseguire rispettivamente il fetch e la decodifica della futura istruzione da eseguire a seguito di un salto condizionato. In un flusso normale, le istruzioni vengono eseguite una dopo l’altra e, quindi, nel momento in cui la CPU sta completando l’esecuzione dell’istruzione “corrente”, nella pipeline sono presenti le successive 5 istruzioni parzialmente in lavorazione.

La “magia” (e, soprattutto, le prestazioni) si spezza a seguito dei famigerati salti, e in particolare di quelli che non sono prevedibili, in quanto la diramazione avviene soltanto se si verifica una certa condizione. In questo caso la pipeline dev’essere “svuotata” (come si dice in gergo), cioè tutto il lavoro fatto per le rimanenti istruzioni va annullato e dev’essere “riempita” ripartendo dall’inizio, quindi caricando la prima istruzione utile da eseguire, e così via. In genere la penalizzazione in cicli di clock è pari al numero di stadi della pipeline, ma ciò dipende strettamente dall’implementazione.

Nello specifico, un salto non prevedibile che fa variare il flusso dell’esecuzione dovrebbe richiedere 6 cicli di clock (0 se il flusso non venisse interrotto). Col meccanismo degli stadi shadow, invece, si ha soltanto 1 ciclo di clock di penalizzazione (2 se il salto non avviene), in quanto nella pipeline l’istruzione obiettivo del salto risulta già parzialmente “lavorata”.

Ricapitolando, un salto taken (sempre come si dice in gergo) richiede 2 cicli di clock, mentre se not taken ne prende 3. Tutto sommato si tratta di un buon compromesso (2 / 3, molto più bilanciato di un 6 / 0), in quanto questo microprocessore non è dotato di alcuna logica di predizione dei salti, per cui un salto condizionato in linea teorica ha la stessa probabilità di cambiare il flusso oppure no (in realtà sono leggermente avvantaggiati i salti taken, che richiedono soltanto 2 cicli di clock).

Cambiamenti significativi sono presenti anche nella logica d’interfacciamento col bus esterno. Il 68040 non è in grado, ad esempio, di variare dinamicamente la dimensione del bus rispetto a quella della “porta” (memoria o dispositivo di I/O) con cui sta comunicando.

In altre parole, se esegue un accesso richiedendo un dato di una certa dimensione, la corrispondente porta deve fornirlo della medesima dimensione (prima, invece, CPU e porta si “accordavano” e provvedevano, eventualmente, a eseguire più accessi per soddisfare la richiesta iniziale). Comunque per sistemi che hanno bisogno del precedente meccanismo, Motorola ha realizzato un chip che ne emula il funzionamento.

A fronte di una caratteristica in meno, ne arriva un’altra nuova e decisamente più interessante: lo snooping dei dati. Entrambi i controller della memoria (quello delle istruzioni e quello dei dati) monitorano continuamente il bus analizzando gli accessi effettuati dagli altri dispositivi, ricavando l’indirizzo e controllando se appartiene a uno di quelli presenti nella cache.

Se il risultato è positivo, si tratta di un’operazione di lettura (da parte del dispositivo), e nella cache è presente un dato aggiornato, l’accesso al bus viene bloccato dal processore che provvede egli stesso a fornire il dato al richiedente. Se invece l’operazione è di scrittura, la cache viene aggiornata col nuovo dato che viene successivamente immesso nel bus dalla periferica.

Inutile dire che in ambienti multiprocessore un meccanismo del genere è un’autentica manna dal cielo, visto che consente di interfacciare più CPU o dispositivi con relativamente poca logica (e quindi usando chipset più semplici ed economici), mantenendo la coerenza dei dati nelle rispettive cache.

Altro pezzo forte di questo microprocessore è l’FPU, che adesso si trova integrata e risulta particolarmente veloce. Infatti, com’è possibile vedere dal diagramma interno che si trova all’inizio dell’articolo, quando la logica di decodifica si accorge di essere in presenza di un’istruzione per il coprocessore matematico, la passa immediatamente alla relativa pipeline, che provvede a elaborarla in maniera indipendente dal flusso delle istruzioni (salvo in caso di eccezioni sollevate o dipendenza dei dati).

Purtroppo, come per le MMU, sono stati apportati dei “tagli” a quest’unità, che non permette di utilizzare il formato packed BCD e supporta soltanto un ristretto insieme di operazioni (soltanto quelle più comuni):

Si tratta si scelte dolorose, ma condivisibili alla luce dei risultati. Il 68040, infatti, eroga 3,5 MFLOPS: più di 10 volte rispetto al coprocessore 68882 di pari frequenza (che arriva a poco più di 0,5 MFLOPS nella sua versione più veloce), e Motorola garantisce che l’emulazione delle istruzioni mancanti, tramite un’apposita libreria che fornisce, risulta mediamente più veloce.

Qui è bene fare una riflessione. A differenza di Intel, che s’è trascinata dietro qualunque aspetto delle varie versioni dell’ISA x86 che si sono succedute nel tempo, Motorola con la sua famiglia 68000 ha deciso, a partire dal 68030, di sacrificare l’assoluta compatibilità col passato in cambio di un’architettura che le rendesse la vita più facile (ed economica) nella corsa alle prestazioni.

Si giustifica in questo modo l’eliminazione del supporto ai “moduli” (istruzioni CALLM/RTM) introdotto col 68020, ma defenestrato già col modello successivo, e il sottoinsieme di funzionalità implementato nell’MMU integrata del 68030. Il 68040 rimarca il concetto, con delle MMU ancora più semplificate e incompatibili, un’FPU a cui mancano diversi pezzi, e alcuni “stack frame” (di cui abbiamo parlato nell’articolo sul 68020) che sono stati eliminati (e altri, nuovi e più semplici, aggiunti).

Da programmatore sono scelte che non condivido, perché l’ISA rappresenta una sorta di “contratto” che il produttore della CPU stipula con chi la dovrà programmare, e chi sviluppa software si aspetta che, tenendo conto delle debite circostanze, possa girare su qualunque modello della famiglia. Per contro, da utente mi aspetto prestazioni sempre più elevate.

Raggiungere le prestazioni, però, richiede compromessi che a volte sono difficili da realizzare e/o digerire. Guardando al 68040 sembra che, tutto sommato, si possa chiudere un occhio davanti ai favolosi 20 MIPS che eroga a 25MHz. Tanto tutte le cose che mancano possono venire emulate, a quanto sembra. Peccato che ciò possa avvenire soltanto in presenza di un sistema operativo. aggiornato che provveda a riconoscere la CPU su cui gira, intercettare le eccezioni scatenate dalle istruzioni mancanti, e a provvedere di conseguenza.

Questo, però, non è sempre possibile. Presuppone l’aggiornabilità del s.o., condizione che non sempre si può verificare: basti pensare a quelli che risiedono su ROM, com’era molto comune all’epoca. Peggio ancora, presuppone la presenza di un s.o. e, sembrerà assurdo, anche questo non è affatto garantito!

Chi ha sviluppato giochi per Amiga sa che la stragrande maggioranza di essi “uccideva” il s.o., per impadronirsi poi di tutte le risorse del sistema, tabella delle eccezioni inclusa. Dunque i pezzi che mancano in questi casi non possono essere sostituiti in maniera trasparente, quindi se c’è qualche porzione di codice che fa uso di qualcuna delle caratteristiche assenti, semplicemente… non girerà. Con buona pace dei propositi di Motorola.

A ciò aggiungiamo che, manuale utente alla mano, la realtà risulta ben diversa. 20 MIPS a 25Mhz significa eseguire quasi un’istruzione per ciclo di clock, ma le tabelle degli opcode mostrano tutt’altro scenario. Anzi, si arriva tranquillamente a decine di cicli di clock per alcune istruzioni particolarmente “complesse”.

Motorola ha, dunque, mentito? Non esattamente. La situazione è simile a quella di tanti altri microprocessori, come lo SPARC v7 di cui parlavo prima che, essendo RISC, dovrebbe eseguire anch’esso le istruzioni in più o meno 1 ciclo di clock. Sono soltanto le istruzioni più “semplici” a godere di questa proprietà.

Poiché spesso sono anche le più comuni, ecco spiegato il perché di quei 20MIPS. Certo, dipende sempre dall’applicazione, perché il codice in giro è piuttosto variegato, per cui ci si affida a stime, basate magari sull’analisi degli opcode che si trovano nei software più comuni e utilizzati.

Ad esempio, se avessero dovuto fare i conti con l’emulatore 80186 che avevo scritto per gli Amiga dotati almeno di un 68020, quel numero sarebbe stato decisamente più basso, a causa delle istruzioni “esotiche” e delle modalità d’indirizzamento indirette che utilizzavo massicciamente.

Ma un’applicazione scritta in assembly e ottimizzata accuratamente a manina non è un campione rappresentativo, specialmente perché la tendenza è stata quella di affidarsi ai compilatori di linguaggi ad alto livello, che non possono prevedere l’impiego di qualunque caratteristica anche oscura che l’ISA di un processore può mettere a disposizione, se ciò non è riconducibile a pattern predefiniti (da chi ha scritto il compilatore).

Una ricerca delle migliori combinazioni di opcode basata sulla forza bruta non sarebbe proponibile, perché i pattern da testare arrivano anche a miliardi per piccolissime porzioni di codice. Né è ipotizzabile, almeno al momento, un sistema che riesca a comprendere l’algoritmo che ha implementato il programmatore, e tirare fuori trucchetti “fantasiosi” (come, ad esempio, quello menzionato dal lettore pleg nel commento #17 a un precedente articolo) per sfruttare il più possibile ciò che la CPU offre.

Il risultato è che i compilatori difficilmente riescono a tirare fuori istruzioni rare e/o complesse, per cui il codice generato sarà abbastanza semplice da essere velocemente digeribile dal microprocessore su cui girerà.

In definitiva la via rimane quella tracciata dai RISC: accelerare il più possibile le istruzioni più “semplici” e comuni, tagliando o non sprecando risorse per fare lo stesso con le altre. Motorola ne era cosciente, ed è per questo che, nonostante il 68040 sia il rappresentante di una delle famiglie “più CISC”, riesce a ottenere mediamente prestazioni molto elevate.

Col successivo 68060 la linea rimane la stessa, ma vedremo che, purtroppo, sarà destinata a interrompersi bruscamente…

13 Commenti »

I commenti inseriti dai lettori di AppuntiDigitali non sono oggetto di moderazione preventiva, ma solo di eventuale filtro antispam. Qualora si ravvisi un contenuto non consono (offensivo o diffamatorio) si prega di contattare l'amministrazione di Appunti Digitali all'indirizzo info@appuntidigitali.it, specificando quale sia il commento in oggetto.

  • # 1
    j
     scrive: 

    complimenti Cesare, ottimo articolo, chiaro e assai interessante – e grazie
    anche perchè il passaggio sugli shadow stages dà un’ idea di come la penalità da branch misprediction non sia intrinsecamente funzione dei soli stadi di pipeline, ma del numero di stadi e della concezione della pipeline e degli eventuali accorgimenti atti a mitigarla (o loro assenza in caso di pipeline “stupida”…)
    e questo è molto importante, soprattutto per i non addetti ai lavori e/o chi bazzicando forum sia esposto alla concezione comune purtroppo riportata anche da da utenti esperti di forum tecnici (“tot stadi uguale tot cicli persi”, quante volte capita di leggerlo…)

  • # 2
    Cesare Di Mauro (Autore del post)
     scrive: 

    Non ti nascondo che parecchio tempo fa pensavo la stessa cosa.

    Poi studiando le architetture ho appreso che le implementazioni non sono tutte uguali, e che esistono soluzioni diverse agli stessi problemi.

    Ma nel caso del 68060, ad esempio, che si discosterà dal 68040. ;)

  • # 3
    Warfox
     scrive: 

    Maledetto il giorno in cui motorola ha deciso di darsi ai ppc prima e ai cellulari poi.

  • # 4
    Caterpillar
     scrive: 

    Che palle, ogni volta che leggo sull’architettura 68000 rosico perchè siamo costretti ad usare processori x86. Anche volendo cambiare sarebbe impossibile perchè tutte le schede che usiamo (grafiche, audio, blabla) son progettate per girare con tale architettura.
    AMD salvaci tu ed inventati qualcosa

  • # 5
    Marco71
     scrive: 

    Buona sera Dr. Di mauro…inutile dirle che gli articoli da lei scritti sulla “famigliola” 68000 li ho letteralmente assimilati alla singolarità (Borg)…
    Nella pila di numeri di MC Microcomputer e Bit che ho, ne conservo uno in particolare che venero come una reliquia proveniente dal continente perduto Mu…
    Su tale numero di Bit si parlava per l’appunto di un confronto abbastanza approfondito tra iAPX 80486 ed MC68040 a 25MCicli come direbbe James Bond.
    Non mi è mai riuscito trovare documentazione sul controlore di cache memory integrato nell’80486 e derivato dal glorioso 82385…sigh.
    Che tristezza la f.p.u a ranghi ridotti dell’MC68040…
    Intel almeno da questo punto di vista integrò un “intero” 80387 istruzioni trascendenti comprese…
    Grazie.

    Marco71.

  • # 6
    pleg
     scrive: 

    @ j

    Si’ e’ un errore comune. Ma per fortuna, per un pipeline semplice (singola, in-order) come quella mostrata in questo articolo, c’e’ un modello e una formula di validita’ generale: il modello producer-consumer.

    Ovvero: alcuni stadi di pipeline sono dei producer (producono risultati, vuoi dati o controlli) e altri sono consumer (consumano quei dati o quei controlli). Naturalmente uno stadio puo’ essere entrambi.

    A questo punto, e’ facile vedere come il numero di clock di penalita’ sia semplicemente la differenza tra lo stadio producer e lo stadio consumer. Esempio per la penalita’ di branch: il branch viene “consumato” dallo stadio 1 (il fetch), e supponiamo essere prodotto allo stadio 3: 3-1=2, e quindi hai 2 cicli di clock di penalita’ per un branch taken.

    Per questo vuoi cercare di avere il calcolo del branch il prima possibile, per ridurre quel numero: la pipeline potrebbe anche essere lunga 20 stadi, ma se hai il risultato del branch disponibile allo stadio, ad esempio, 4, la penalita’ e’ solo 3 cicli. Questa tecnica, e altre, fanno parte della cosiddetta “compressione del front-end”: comprimere il fetch delle istruzioni e la sua logica di controllo nel minor numero di stadi possibile, alla testa della pipeline: questo riduce la penalita’ di branch misprediction.

    (naturalmente questo non e’ in generale possibile: se hai una pipe con 20 stadi e’ perche’ la pipe e’ affettata in fette molto sottili, e quindi anche la branch resolution andra’ a finire in qualche stadio avanti).

    Nota che questa formula e’ assolutamente generale. La puoi applicare anche ad esempio alla ALU: se hai una ALU a 3 stadi, e due istruzioni back-to-back (la prima istruzione produce il risultato per la seconda), il risultato e’ prodotto 3 stadi dopo, e quindi hai un ritardo di 3 cicli tra le due operazioni (questo assumendo che la pipe abbia forwarding paths, se no la penalita’ e’ molto peggiore).

  • # 7
    pleg
     scrive: 

    @ Cesare

    Com’e’ possibile che un salto non-taken abbia 3 cicli di penalita’? Un salto non-taken e’, appunto, un non-salto, quindi continui a fare il fetch dallo stesso flusso di istruzioni.

    Vuoi dire che stallava la pipeline ad ogni salto, fino a quando non veniva risolto? Non aveva un meccanismo di pipe-flush per recuperare da un flusso errato? Mi sembra incredibile.

  • # 8
    Cesare Di Mauro (Autore del post)
     scrive: 

    Intanto ti ringrazio per le spiegazioni molto chiare sul funzionamento di una pipeline riguardo alle penalizzazioni. :)

    Purtroppo ci sono poche informazioni a riguardo, e il manuale per l’utente in questione non si sofferma su questi dettagli. Ti riporto di seguito quello che ho trovato:

    Conditional branches are optimized for the more common case of the branch taken, and both execution paths of the branch are fetched and decoded to minimize refilling of the instruction pipeline.
    […]
    To achieve maximum performance, the processor prefetches the instructions of both paths for a conditional branch. If the conditional branch results in a branch-not-taken, the previously issued branch-taken prefetch is then terminated since the prefetch is no longer needed.
    […]
    The IU uses the instruction cache to store instruction prefetches as it requests them. Instruction prefetches are normally requested from sequential memory locations except when a change of program flow occurs (e.g., a branch taken) or when an instruction that can modify the status register (SR) is executed, in which case the instruction pipe is automatically flushed and refilled.

    Dalla tabella dei tempi d’esecuzione delle istruzioni:

    Bcc Branch Taken 2
    Bcc Branch Not Taken 3

    DBcc False, Count > –1 3
    DBcc False, Count = –1 4
    DBcc True 4

    La cosa strana riguarda i salti condizionati delle istruzioni dell’FPU:

    FBcc Taken 7
    FBcc Not Taken 6

    FDBcc cc True 9
    FDBcc cc False 11

    dove la situazione s’inverte e i salti taken risultano penalizzati, ma solo nel classico salto condizionato (non in quello utilizzato nell’istruzione di loop con contatore, la FDBcc).

    Sembra che, di fronte a un salto condizionato, l’integer unit assuma sempre come valido il cambiamento di flusso, eseguendo il prefetch a partire dal target del salto, salvo poi abortire l’operazione se si accorge che il salto non è taken.

    Come abbiano implementato tutto ciò mi è del tutto ignoto.

    Ringrazio tutti per i complimenti.

    @Marco71: prima o poi mi toccherà trattare anche l’80486, per cui cercherò un po’ di documentazione anche sul suo memory controller.

    Sì, è un vero peccato che Motorola abbia mortificato l’FPU, ma evidentemente avrebbe richiesto troppi transistor e la complessità della pipeline dedicata non avrebbe permesso di ottenere prestazioni così elevate.

    L’unica cosa positiva in questo è che tutti i programmi che sfruttavano l’FPU giravano sull’AmigaOS, per cui era possibile utilizzare la libreria che Motorola aveva messo a disposizione.

  • # 9
    pleg
     scrive: 

    In effetti e’ oscuro: quello che dice il manuale e’ semplicissimo, ma non si accorda con le branch penalties.

    E’ vero che la condizione piu’ comune e’ branch taken, ed e’ una scelta condivisibile che in una pipeline del genere, in assenza di branch prediction (che e’ una tecnica piu’ difficile, ma piu’ efficace), si seguano entrambi i percorsi del branch, tenendo poi quello corretto (basta un flush del paio di stadi che sono andati nella direzione sbagliata, tenendo buono tutto il resto).

    Ma proprio per questo mi aspetterei che un branch non-taken abbia penalita’ 0, come e’ in genere il caso in pipeline semplici.

    Hai il link del manuale? Gli darei un’occhiata per cercare di chiarire il mistero.

  • # 10
    Cesare Di Mauro (Autore del post)
     scrive: 

    Ho le tue stesse perplessità.

    Il manuale lo trovi qui: http://www.freescale.com/files/32bit/doc/ref_manual/MC68040UM.pdf?fpsp=1

  • # 11
    pleg
     scrive: 

    Grazie del manuale. A quanto vedo e’ anche multicycle, cioe’ le istruzioni possono restare per piu’ cicli nello stesso stage di pipeline per eseguire… mi ero dimenticato di questa possibilita’ :P

    Comunque, il manuale non dice perche’ i branch debbano restare in per 2 e 3 cicli rispettivamente. E cosi’ su due piedi non saprei dire, bisognerebbe vedere i dettagli.

  • # 12
    Cesare Di Mauro (Autore del post)
     scrive: 

    Sì, è necessario perché, ad esempio, ha delle modalità d’indirizzamento estremamente complesse (quelle indirette in particolare), e l’unità di calcolo dell’indirizzo non riuscirebbe ricavarlo in un solo ciclo di clock.

    Purtroppo quello è un manuale utente, e difficilmente si trovano dettagli di così basso livello.

  • # 13
    iva
     scrive: 

    Bell’articolo come sempre e molto interessanti le discussioni successive!

Scrivi un commento!

Aggiungi il commento, oppure trackback dal tuo sito.

I commenti inseriti dai lettori di AppuntiDigitali non sono oggetto di moderazione preventiva, ma solo di eventuale filtro antispam. Qualora si ravvisi un contenuto non consono (offensivo o diffamatorio) si prega di contattare l'amministrazione di Appunti Digitali all'indirizzo info@appuntidigitali.it, specificando quale sia il commento in oggetto.