Università degli Studi di Parma DIPARTIMENTO DI MATEMATICA E INFORMATICA Corso di Laurea in Informatica Tesi triennale Progettazione e realizzazione di un emulatore funzionale di componenti TTL per architetture complesse in linguaggio Java Design and implementation of a functional emulator of TTL components for complex architectures in Java Candidato: Relatore: Laura Savino Prof. Bergenti Federico Matricola 212375 Anno Accademico 2012–2013 Indice Indice i Prefazione Tecnologia TTL e la serie 74xx . . . . . . . . . . . . . . . . . . . . . CPU di Eckert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ii iii iv 1 . . . . . . . . 1 2 3 4 5 6 7 9 11 . . . . . . . . . . . . 13 14 14 17 18 20 22 25 28 33 38 51 52 2 Componenti Codifica dei componenti TTL TTL 7400 . . . . . . . . . . . . TTL 7402 . . . . . . . . . . . . TTL 7474 . . . . . . . . . . . . TTL 74138 . . . . . . . . . . . TTL74181 . . . . . . . . . . . . EIA RS-232 . . . . . . . . . . . MAX232 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sviluppo del codice Connessione . . . . . . . . . . . . . . . . . La classe SimulatedConnection . . . Clock . . . . . . . . . . . . . . . . . . . . . La classe SimulatedClock . . . . . . Emulazione componenti TTL . . . . . . . La classe SimulatedTTL7400 . . . . . La classe SimulatedTTL7402 . . . . . La classe SimulatedTTL7474 . . . . . La classe SimulatedTTL74138 . . . . La classe SimulatedTTL74181 . . . . Comunicazione RS232 - architettura TTL . La classe SimulatedMAX232 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Conclusioni e sviluppi futuri 56 Bibliografia 58 i Prefazione Il seguente lavoro di tesi si propone di illustrare il processo di realizzazione per l’emulazione di una CPU secondo l’architettura di Eckert. Per giungere a tale scopo si sono dapprima emulati una serie di componenti TTL per poi combinarli e raggiungere una simulazione funzionale per un’architettura complessa. La scelta di tale argomento è stata dettata da diversi motivi: - un interesse personale della sottoscritta, decisa ad approfondire alcuni aspetti come il funzionamento a livello hardware di una CPU e l’interazione software-hardware di un emulatore; - un interesse accademico, in quanto solitamente l’interesse per emulazioni di componenti TTL va a ricercare una profonda precisione ma in architetture semplici, piuttosto che sviluppare un interesse solo a livello funzionale, ma di oggetti più completi. In seguito ad uno studio preliminare dei concetti di base necessari all’approccio del progetto, si è conclusa la necessità di implementare emulatori funzionali per ciascuno dei seguenti: - un ALU; - 5 diversi registri registri; - una RAM; - una ROM; - il bus di collegamento. Inoltre si è deciso di dare la possibilità di accedere all’emulatore anche dalla rete andando ad implementare l’emulatore per una porta seriale RS-232. Giunti a questo punto si è cominciato a selezionare i componenti ritenuti necessari per la realizzazione del progetto descritti nel capitolo 1 per poi passare all’implementazione di essi come analizzato nel capitolo 2. ii Prefazione Tecnologia TTL e la serie 74xx Per la realizzazione funzionale dell’emulatore si è scelto di utilizzare componenti Transistor-Transistor Logic inventata nel 1961. Già dal 1963 sono stati prodotti i primi circuiti integrati commerciali con dispositivi TTL e a partire dal 1964 la Texas Instruments introdusse sul mercato la serie 5400 e successivamente la serie 7400, una famiglia di circuiti integrati comprendente un’ampia gamma di funzioni logiche. La serie 7400 (anche nota come serie 74xx, dal prefisso costante dato ad ogni componente) divenne uno standard industriale, e molti, tra le quali Motorola e National Semiconductor, cominciarono produrla; mentre alcune industrie produssero dispositivi in questa tecnologia, molti dei quali compatibili in modo diretto con la serie 7400, ma definendoli con sigle proprietarie, come la serie 9300 di Fairchild Semiconductor. Vi furono diverse versioni successive di questa famiglia: - la LS, relativamente veloce con consumi di corrente ridotti, - la S, caratterizzata da maggiore velocità di commutazione a scapito del consumo di corrente - la C, direttamente in concorrenza con la serie 4000 in tecnologia CMOS, (stessa funzione con identica piedinatura) e consumi estremamente bassi - la versione military con sigla 54xx, caratterizzata da un più ampio margine di temperatura di lavoro garantita (-55 125 C° a differenza della prima, limitata a 0 75 C°). Il materiale del package di questa famiglia (come in altre), poteva essere in resina o ceramica (per la classe di temperatura con margine più esteso). La TTL è una tecnologia importante ed estremamente diffusa perché grazie al suo basso costo rese economico l’utilizzo di approcci digitali laddove prima si utilizzavano metodi analogici. Per fare alcuni esempi, il Kenbak1, uno dei primi Personal Computer, usava chip TTL al posto di un singolo microprocessore (che non esisteva ancora nel 1971) e uno degli ultimi computer ad utilizzare questa famiglia di chip fu l’Olivetti P6060 (presentato nel 1975), il quale aveva la CPU realizzata su una coppia di schede in vetro. iii Prefazione CPU di Eckert Figura 1: CPU di Eckert La CPU di Eckert è costituita da - un ALU a 12 bit con due registri dedicati anch’essi a 12 bit; - una RAM a 8 bit anch’essa con due registri dedicati, entrambi a 12 bit; - un PC a 8 bit; - una ROM anch’essa a 8 bit(non visibile nell’immagine); - e un IR a 12 bit, - il tutto collegato da un bus che può essere a 12 bit oppure a 8 bit; dove i comandi in figura 1 corrispondono alle seguenti funzioni: L (load) riempie il registro (carica); E (enable) scrive il contenuto del registro sul bus; A esegue una somma; S calcola una differenza. iv 1. Componenti Per la realizzazione di questo lavoro si è scelto di utilizzare la famiglia di circuiti integrati TTL 74xx per tre motivi principali: - innanzitutto costituì uno standard de-facto nella disposizione della piedinatura (le varie famiglie con prestazioni migliorate sia nel consumo di corrente che in velocità di commutazione che succedettero a questa, tranne qualche eccezione, mantennero la compatibilità della piedinatura); - inoltre è composta da centinaia di dispositivi aventi tutte le funzioni necessarie: dalle porte logiche di base, ai i flip-flop, passando per i contatori, i transceiver di bus e le unità aritmetiche e logiche (ALU); - infine i componenti della serie sono facilmente reperibili in una qualche versione delle precedenti descritte. Prima di arrivare a considerazioni relative al codice elaborato, nel capitolo successivo, andiamo ad esplorare un minimo come riconoscere un componente della serie in analisi, nonché il funzionamento logico dei componenti che si è scelto di utilizzare in questo lavoro. Infine descriveremo come sia possibile trasmettere i dati ottenuti dal nostro insieme di componenti a logica TTL con uno standard di tipo diverso, nello specifico l’RS-232. 1 Componenti Codifica dei componenti TTL Figura 1.1: Circuito integrato TTL7400 Guardando la figura 1.1 che illustra il primo integrato della serie, il 7400 (leggi 74-zero-zero), possiamo andare a descrivere la codifica standardizzata della serie: - logo del costruttore (nella figura a destra: Texas Instruments); - una o più lettere che identificano il costruttore (nella figura SN); - il numero 74, tipico della famiglia logica; - il numero 00, in altri casi costituito da più di due cifre, indicante le funzioni logiche svolte dal dispositivo; - una o più lettere indicanti il tipo di contenitore del dispositivo (PTH o SMD) (in figura N che indica un dispositivo detto DIP Dual inline package, sempre meno usato nelle grosse produzioni e sostituito dalla versione SMD); - segue, nella seconda riga, un codice che in genere indica settimana e anno di produzione (nella figura la 45° settimana dell’anno 1976); - Sul retro dell’integrato vi sono talvolta altre indicazioni (lotto, stabilimento di produzione ed eventuali altre informazioni). 2 Componenti TTL 7400 Il primo della serie, l’integrato digitale 7400, è costituito da 14 piedini. Tra essi abbiamo Vcc (14) collegato all’alimentazione +5V e GND (7) collegato a massa, per poter alimentare i transistor che formano il circuito delle porte logiche, mentre gli altri sono suddivisi come: Pin Nome Funzione (1) (2) (4) (5) (8) (9) (11) (12) D0a D0b D1a D1b D2a D2b D3a D3b INPUT INPUT INPUT INPUT INPUT INPUT INPUT INPUT Pin Nome Funzione (3) (6) (10) (13) Q0 Q1 Q2 Q3 OUTPUT OUTPUT OUTPUT OUTPUT Il 7400, contiene 4 porte logiche NAND, ciascuna indipendente rispetto alle altre. Realizza quindi 4 NAND su coppie di dati Da, Db in ingresso, restituendo il risultato sull’output Q. 3 Componenti TTL 7402 L’integrato digitale 7402 è costituito da 14 piedini. Come prima, tra essi abbiamo Vcc (14) collegato all’alimentazione +5V e GND (7) collegato a massa, per poter alimentare i transistor che formano il circuito delle porte logiche, mentre gli altri sono suddivisi come: Pin Nome Funzione (2) (3) (5) (6) (8) (9) (11) (12) D0a D0b D1a D1b D2a D2b D3a D3b INPUT INPUT INPUT INPUT INPUT INPUT INPUT INPUT Pin Nome Funzione (1) (4) (10) (13) Q0 Q1 Q2 Q3 OUTPUT OUTPUT OUTPUT OUTPUT Il 7402, contiene 4 porte logiche NOR, ciascuna indipendente rispetto alle altre. Realizza quindi 4 NOR su coppie di dati Da, Db in ingresso, restituendo il risultato sull’output Q. 4 Componenti TTL 7474 Il 7474 è un dual positive edge triggered D-type flip-flop con 14 piedini. Ovviamente tra essi abbiamo Vcc (14) collegato all’alimentazione +5V e GND (7) collegato a massa, per poter alimentare i transistor che formano il circuito delle porte logiche, mentre gli altri sono suddivisi come: INPUT Pin Nome Funzione (2) (12) (3) (11) (4) (10) (1) (13) D0 D1 CP0 CP1 SD0 SD1 RD0 RD1 dato dato CLOCK CLOCK SET SET RESET RESET OUTPUT Pin Nome Funzione (5) (6) (8) (9) Q0 Q0 Q1 Q1 risultato risultato risultato risultato Con il 7474 abbiamo che il dato in ingresso D, che soddisfa il set-up e mantiene il clock in una transizione verso l’alto (low-to-high), viene memorizzato nel flip-flop e appare in output su Q. 5 Componenti TTL 74138 L’integrato digitale 7402 è costituito da 16 piedini e va a realizzare un decoder/demultiplexer con 3 linee in entrata e 8 in uscita. Come prima, tra essi abbiamo Vcc (14) collegato all’alimentazione +5V e GND (7) collegato a massa, per poter alimentare i transistor che formano il circuito delle porte logiche, mentre gli altri sono suddivisi come: Pin Nome Funzione (1) (2) (3) (5) (6) (8) A B C G2A G2B G1 SELEZIONE SELEZIONE SELEZIONE AUTORIZZAZIONE AUTORIZZAZIONE AUTORIZZAZIONE Pin Nome Funzione (7) (9) (10) (11) (12) (13) (14) (15) Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 OUTPUT OUTPUT OUTPUT OUTPUT OUTPUT OUTPUT OUTPUT OUTPUT Il decoder 74138 accetta tre ingressi di indirizzamento ponderati binari (A, B e C) e, quando attivato, fornisce 8 uscite active low (da Y0 a Y7) mutuamente esclusive. 6 Componenti TTL74181 Il 74181 è un ALU (Aritmethic Logic Unit) a 24 piedini. Di nuovo, tra essi abbiamo Vcc (14) collegato all’alimentazione +5V e GND (7) collegato a massa, per poter alimentare i transistor che formano il circuito delle porte logiche, mentre gli altri sono suddivisi come: INPUT Pin Nome Funzione (1) (2) (18) (19) (20) (21) (22) (23) B0 A0 B3 A3 B2 A2 B1 A1 operando operando operando operando operando operando operando operando INPUT Pin Nome Funzione (3) (4) (5) (6) (7) (8) S3 S2 S1 S0 Cn M SELEZIONE SELEZIONE SELEZIONE SELEZIONE SELEZIONE SELEZIONE OUTPUT Pin Nome Funzione (9) (10) (11) (13) (14) (15) (17) (16) F0 F1 F2 F3 A=B P G Cn+4 risultato operazioni risultato operazioni risultato operazioni risultato operazioni comparatore propagatore di riporto generatore di riporto riporto 7 Componenti L’ALU 74181 contiene l’equivalente di 75 porte logiche ed è in grado di eseguire tutte le tradizionali operazioni di somma/sottrazione/decremento con o senza riporto, così come AND/NAND,OR/NOR, XOR, e lo spostamento per un totale di 16 operazioni aritmetiche e 16 operazioni logiche tra parole di 4 bit. Sfortunatamente le operazioni di moltiplicazione e divisione non sono direttamente fornite, ma possono essere rese in step successivi utilizzando funzioni di shifts, somme e sottrazioni. Allo stesso modo lo shift non è realizzato come funzione esplicita ma può essere derivato da alcune funzioni come A plus A, dove plus è l’operazione di somma aritmetica. Il 74181 realizza queste operazioni su due operandi da 4 bit generando un risultato con riporto (in 22 nanosecondi). 8 Componenti EIA RS-232 (a) Connettore maschile (b) Connettore femminile Figura 1.2: Disposizione pin porta seriale Dal punto di vista hardware, una porta realizza di fatto un’interfaccia tra una macchina e altri computer o periferiche: • fisicamente, una porta è un’uscita specializzata su una parte di apparecchio a cui viene collegata una spina o un cavo; • elettronicamente, i numerosi conduttori che compongono la presa forniscono un trasferimento del segnale tra i dispositivi. L’EIA RS-232, o più semplicemente RS-232 o porta seriale, è uno standard europeo che definisce un’interfaccia seriale a bassa velocità di trasmissione per lo scambio di dati tra dispositivi digitali. 9 Componenti La comunicazione definita in tale standard è di tipo seriale asincrona: • seriale perché le informazioni vengono inviate un bit alla volta; • asincrona poiché l’invio non avviene in intervalli di tempo predefiniti; dunque il trasferimento dei dati può iniziare in qualsiasi momento ed è compito del ricevitore rilevare quando un messaggio inizi e termini. Nel caso specifico si è presa in considerazione una porta con 9 piedini, sia per quanto riguarda il connettore maschio che per quello femmina (come indicato in figura 1.2); in realtà i pin che vengono considerati nell’utilizzo pratico sono solamente 3 (il secondo, il terzo e il quinto), come vedremo più avanti. Pin Descrizione 1 Portante in ricezione presente 2 Dati in ricezione 3 Dati in trasmissione 4 Dispositivo terminale pronto 5 Massa dei segnali 6 Dispositivo di comunicazione pronto 7 Richiesta di trasmissione 8 Pronto a trasmettere 9 Chiamata in arrivo 10 Componenti MAX232 Figura 1.3: MAX232 della Maxim Integrated Products La scelta dell’oggetto da emulare è ricaduta sul circuito integrato MAX232, creato nel 1987 da Maxim Integrated Products, che converte i segnali da una porta seriale RS-232 in segnali adatti per l’uso in TTL circuiti logici digitali compatibili. Figura 1.4: Circuito integrato MAX232 Il MAX232 è costituito da 16 pins e dispone di due canali per la comunicazione RS-232 bidirezionali, nello specifico la tabella seguente spiega brevemente il significato della piedinatura. 11 Componenti Pin Descrizione 1 Polo positivo del condensatore per il convertitore a pompa di carica per generare la tensione positiva 2 Uscita tensione positiva dello stadio di duplicazione della tensione di alimentazione 3 Polo negativo del condensatore per il convertitore a pompa di carica per generare la tensione positiva 4 Polo positivo del condensatore per il convertitore a pompa di carica per generare la tensione negativa 5 Polo negativo del condensatore per il convertitore a pompa di carica per generare la tensione negativa 6 Uscita tensione negativa dello stadio dell’inversione della tensione di alimentazione 7 Uscita RS-232 canale 2 8 Ingresso RS-232 canale 2 9 Uscita TTL/CMOS canale 2 10 Ingresso TTL/CMOS canale 2 11 Ingresso TTL/CMOS canale 1 12 Uscita TTL/CMOS canale 1 13 Ingresso RS-232 canale 1 14 Uscita RS-232 canale 1 15 Segnale di massa 16 Positivo all’alimentazione Inoltre, il MAX232, richiede solo +5Vcc anche per supportare, in trasmissione, lo standard RS-232. Ciò è possibile, grazie a due stadi convertitori DC-DC, ovvero un elevatore di tensione a capacità, da +5Vcc a +12Vcc; cui segue uno stadio invertitore di polarità, sempre a capacità, da +12Vcc a −12Vcc. Queste tensioni, poi sono poi rese disponibili per altri impieghi al pin 2 (+12Vcc) e al pin 6 (−12Vcc). Infine, i condensatori presenti collegati al circuito integrato permettono il regolare funzionamento degli stadi convertitori DC-DC1 . 1 Circuito che converte una sorgente di corrente continua da una tensione a un’altra. 12 2. Sviluppo del codice Il codice è stato realizzato utilizzando il linguaggio Java, mentre come ambiente di sviluppo integrato per la stesura e la strutturazione del progetto si è scelto di utilizzare l’IDE open source Eclipse. La parte di sviluppo del codice si è divisa in blocchi, rispecchiati da una suddivisione interna dell’intero progetto in pacchetti (laddove il progetto di base è raggruppato a sua volta nello specifico pacchetto da noi definito com.bergenti.fmachines), distribuiti per la realizzazione di: • alcuni modelli relativi all’emulazione hardware [pacchetto .model]; • un interfaccia specifica per ciascun componente TTL che deve essere emulato [pacchetto .model.ttl]; • due interfacce e due classi relative alla creazione e manipolazione di oggetti emulati [pacchetto .simulator]; • alcuni modelli relativi alla creazione e manipolazione per l’emulazione hardware [pacchetto .simulator.model]; • emulatori di tipo funzionale per ciascun componente TTL necessario alla realizzazione dell’architettura [pacchetto .simulator.model.ttl]. Tramite tale suddivisione siamo andati a creare la struttura del progetto di tesi, dopodiché l’abbiamo utilizzata per andare ad emulare i diversi componenti. Proseguiamo ora andando ad esporre ed analizzare la gerarchia e il funzionamento delle classi implementate, tenendo conto del fatto che alcune di esse (e alcune loro funzioni) presentate non verranno esaminate in quanto la denominazione risulta essere un completo commento alle loro semplici funzionalità. 13 Sviluppo del codice Connessione Per la realizzazione di un emulatore dal punto di vista hardware, risultava necessaria l’implementazione di un simulatore per la connessione dei componenti dell’architettura. Essa è stata realizzata, come visibile in figura 2.1, tramite un’architetture a quattro livelli costituiti da classi così suddivise: 1. • un’interfaccia di definizione dei metodi basilari per la gestione della connessione [Connection]; 2. • una classe astratta che implementa l’interfaccia Connection con due campi protetti, costruttore e due funzioni di sovrascrittura per i metodi dell’interfaccia madre[AbstractConnection]; • un’interfaccia che eredita dall’interfaccia Connection per la gestione del contatto [Contact]; 3. • una classe Java che realizza funzionalmente l’implementazione della connessione [SimulatedConnection]. • un’interfaccia identificativa della messa a terra che eredita dall’interfaccia Contact [Ground]; • una classe astratta che implementa la classe Contact con un campo privato, costruttore, un metodo di sovrascrittura rispetto alla funzione dell’interfaccia Contact e un metodo proprio; • un’interfaccia che definisce tre metodi base per l’emulazione di un pin. 4. una classe astratta che eredita dalla classe AbstractContact ed implementa l’interfaccia Pin con un campo privato, costruttore e una funzione di sovrascrittura per il metodo getNumber dell’interfaccia Pin. Andiamo ora ad analizzare la classe che realizza funzionalmente la connessione. La classe SimulatedConnection package com . bergenti . fmachines . simulator . model ; 2 import com . bergenti . fmachines . model . AbstractConnection ; 4 public class SimulatedConnection extends AbstractConnection { Come possiamo vedere, dopo l’ importazione della classe astratta AbstracConnection (e la dichiarazione di appartenenza al pacchetto simulator.model), vediamo, come detto poco sopra, che la nostra classe SimulatedClock estende la classe astratta AbstractConnection ed è priva di campi personali. 14 Figura 2.1: Diagramma delle classi interessate per la gestione e l’emulazione della connessione Sviluppo del codice 15 Sviluppo del codice public SimulatedConnection ( SimulatedContact source , SimulatedContact sink ) { super ( source , sink ) ; 6 8 source . addConnection ( this ) ; 10 sink . addConnection ( this ) ; } 12 Abbiamo poi il costruttore che, presi in input due parametri entrambi di tipo SimulatedContact identificativi rispettivamente della sorgente e della destinazione per la connessione, va a richiamare il costruttore della classe base con i due valori ricevuti e collega la connessione sia alla sorgente che alla destinazione. public void onLevelChanged ( SimulatedContact contact ) { if ( contact == source ) { SimulatedContact other = ( SimulatedContact ) sink ; 14 16 other . setDigitalLevel ( contact . getDigitalLevel () ) ; } else if ( contact == sink ) { SimulatedContact other = ( SimulatedContact ) source ; 18 20 other . setDigitalLevel ( contact . getDigitalLevel () ) ; 22 } } 24 } Infine la classe SimulatedContact possiede il metodo onLevelChanged (personale, non di sovrascrittura) che, preso in input un oggetto di tipo SimulatedContact verifica: • se il parametro in input è uguale alla sorgente allora – inizializza un nuovo oggetto SimulatedContact di destinazione (dopo un opportuno casting); – imposta il livello digitale dell’oggetto creato con quello del parametro in input alla funzione; • se il parametro in input è uguale alla destinazione allora – inizializza un nuovo oggetto SimulatedContact con l’alimentazione (dopo un opportuno casting); – imposta il livello digitale dell’oggetto creato con quello del parametro in input alla funzione. 16 Sviluppo del codice Clock Figura 2.2: Diagramma delle classi per emulazione/gestione del clock Per la realizzazione di un emulatore dal punto di vista hardware, risultava necessaria l’implementazione di un simulatore per il clock dell’architettura. Esso è stato realizzato, come visibile in figura 2.2, tramite: • un’interfaccia di definizione dei metodi basilari per la gestione del clock [Clock]; • un’interfaccia per la gestione delle sorgenti di evento [EventSource]; • una classe astratta che implementa l’interfaccia Clock con un campo protetto, costruttore e una funzione di sovrascrittura per un metodo della classe base[AbstractClock]; 17 Sviluppo del codice • una classe Java che realizza funzionalmente l’implementazione del clock [SimulatedClock]. Andiamo ora ad analizzare proprio quest’ultima classe (la più completa per una breve trattazione riguardo la gestione/emulazione del clock). La classe SimulatedClock package com . bergenti . fmachines . simulator . model ; 2 4 6 8 import import import import com . bergenti . fmachines . model . AbstractClock ; com . bergenti . fmachines . model . Contact ; com . bergenti . fmachines . simulator . EventSource ; com . bergenti . fmachines . simulator . Simulator ; public class SimulatedClock extends AbstractClock implements EventSource { private long elapsedInNanos ; 10 private Contact output ; Dopo le importazioni necessarie (e la dichiarazione di appartenenza al pacchetto simulator.model), vediamo, come detto poco sopra, che la nostra classe SimulatedClock estende la classe astratta AbstractClock e implementa l’interfaccia EventSource; inoltre ha come campi privati due oggetti: • uno di tipo long per misurare il tempo trascorso in nanosecondi • l’altro di tipo Contact [riferimento sezione Connessione] per l’output. 12 public SimulatedClock ( Simulator simulator , String name , int periodInNanos ) { super ( periodInNanos ) ; 14 this . elapsedInNanos = 0; 16 this . output = new SimulatedContact ( simulator , name + " . OUT " ); 18 } Abbiamo poi il costruttore della classe che prende in input tre parametri, nello specifico un oggetto di tipo Simulator e una stringa che serviranno per la costruzione del simulatore, più un intero che identifica la durata del clock in nanosecondi; dopodichè • richiama il costruttore della classe base utilizzando l’ultimo parametro in input; • inizializza il campo relativo al tempo trascorso a zero; • alloca l’oggetto privato di tipo Contact utilizzando i primi due parametri in input e aggiungendo ad essi la stringa identificativa .OUT. 18 Sviluppo del codice @Override public Contact getOutput () { return output ; } 20 22 24 @Override public void timeElapsed ( long timeInNanos ) { elapsedInNanos += timeInNanos ; 26 28 if ( elapsedInNanos >= ( periodInNanos / 2) ) { elapsedInNanos = 0; 30 SimulatedContact simulatedOutput = ( SimulatedContact ) output ; 32 simulatedOutput . toggleDigitalLevel () ; 34 } } 36 } Infine la classe SimulatedContact sovrascrive i metodi: • getOutput dell’interfaccia madre che semplicemente ritorna l’output inizializzato nel costruttore; • timeElapsed dell’interfaccia EventSource che, preso in input un intero rappresentante il tempo trascorso dall’ultima volta che è stata chiamata la funzione (misurato in nanosecondi), va ad aggiornare il campo elapsedInNanos in dipendenza dal parametro passato alla funzione. Dopodiché se il tempo trascorso è maggiore o uguale della metà della durata del clock lo va ad azzerare e, inizializzato un oggetto locale SimulatedContact con il campo output (dopo opportuno casting), va a modificare il livello digitale di quest’ultimo. 19 Sviluppo del codice Emulazione componenti TTL L’emulazione di ciascun circuito integrato descritto nel capitolo 1 è strettamente legata alla gerarchia di tutto il progetto di tesi, escludendo la parte relativa al clock, dunque per avere un’idea della strutturazione alla base delle classi che realizzazione l’emulazione funzionale dei nostri componenti riportiamo in figura 2.3 il diagramma delle classi coinvolte nell’emulazione del circuito integrato, andando a scrivere il termine COMPONENT al posto della siglai di ciascun circuito. Si tratta di una strutturazione a 6 livelli, in cui il primo (più in alto) è costituito solamente da interfacce e alla base della piramide (il sesto livello, in basso) troviamo la nostra classe cumulativa SimulatedCOMPONENT (al pari della classe per la simulazione della fonte di alimentazione). Come si può facilmente notare per rendere possibile la simulazione del circuito integrato di nostro interesse abbiamo precedentemente implementato un’interfaccia che ne descrive la composizione con tutti i suoi pin e le funzioni di base. Andiamo a vedere la realizzazione del codice, secondo la struttura presentata e analizzata, per i componenti che abbiamo realizzato: • 7400 [NAND]; • 7402 [NOR]; • 7474 [FLIP-FLOP per l’implementazione dei registri]; • 74138 [3-8 decoder/DEMULTIPLEXER]; • 74181 [ALU]; • MAX232 [convertitore TTL – RS232]. 20 Figura 2.3: Diagramma delle classi interessate per la gestione e l’emulazione dei componenti TTL Sviluppo del codice 21 Sviluppo del codice La classe SimulatedTTL7400 Analizziamo la classe che implementa un NAND logico su due valori in input per quattro coppie di valori in ingresso. package com . bergenti . fmachines . simulator . model . ttl ; 2 4 6 8 import import import import import import com . bergenti . fmachines . model . Pin ; com . bergenti . fmachines . model . ttl . TTL7400 ; com . bergenti . fmachines . simulator . EventSink ; com . bergenti . fmachines . simulator . Simulator ; com . bergenti . fmachines . simulator . model . SimulatedChip ; com . bergenti . fmachines . simulator . model . SimulatedPin ; Innanzitutto abbiamo le importazioni necessarie per i modelli di riferimento usati nella creazione/utilizzo di tutti gli elementi emulati e la dichiarazione di appartenenza al pacchetto simulator.model.ttl; public class SimulatedTTL7400 extends SimulatedChip implements TTL7400 , EventSink 10 12 public SimulatedTTL7400 ( Simulator simulator , String name ) { super ( simulator , name , Package . DIP , 14) ; for ( int i = 0; i < pins . length ; i ++) { pins [ i ] = new SimulatedPin ( this , name + " . " + PIN_NAMES [ i ] , i + 1) ; } 14 16 } dopodiché possiamo vedere che la nostra classe SimulatedTTL7402 eredita dalla SimulatedChip ed implementa le classi TTL7400 (il modello dell’oggetto) e EventSink, cioè la classe relativa alle modalità di gestione. La classe per l’emulatore non ha campi specifici e quindi ne vediamo direttamente il costruttore; esso • richiama il costruttore della classe base; • crea l’array contente i valori dei corrispondenti pin dell’oggetto fisico. 18 20 @Override public void initialize () { computeOutputs () ; } 22 24 26 @Override public void onLevelChanged ( SimulatedPin pin ) { computeOutputs () ; } Abbiamo poi due funzioni sovrascritte rispetto alla classe base che semplicemente vanno a richiamare il metodo computeOutputs(); la loro implementazione è necessaria in quanto vengono dichiarate all’interno dell’interfaccia TTL7400 implementata dal nostro emulatore. 22 Sviluppo del codice 28 private void computeOutputs () { SimulatedPin a = ( SimulatedPin ) get1A () , b = ( SimulatedPin ) get1B () ; if ( a . isConnected () && b . isConnected () ) { boolean by = !( a . getDigitalLevel () && b . getDigitalLevel () ); 30 32 SimulatedPin y = ( SimulatedPin ) get1Y () ; 34 y . setDigitalLevel ( by ) ; 36 } 38 a = ( SimulatedPin ) get2A () ; b = ( SimulatedPin ) get2B () ; 40 if ( a . isConnected () && b . isConnected () ) { boolean by = !( a . getDigitalLevel () && b . getDigitalLevel () ); 42 SimulatedPin y = ( SimulatedPin ) get2Y () ; 44 y . setDigitalLevel ( by ) ; 46 } 48 a = ( SimulatedPin ) get3A () ; b = ( SimulatedPin ) get3B () ; 50 if ( a . isConnected () && b . isConnected () ) { boolean by = !( a . getDigitalLevel () && b . getDigitalLevel () ); 52 SimulatedPin y = ( SimulatedPin ) get3Y () ; 54 y . setDigitalLevel ( by ) ; 56 } 58 a = ( SimulatedPin ) get4A () ; b = ( SimulatedPin ) get4B () ; 60 if ( a . isConnected () && b . isConnected () ) { boolean by = !( a . getDigitalLevel () && b . getDigitalLevel () ); 62 SimulatedPin y = ( SimulatedPin ) get4Y () ; 64 y . setDigitalLevel ( by ) ; } 66 } La funzione computeOutputs() è la funzione centrale dell’emulatore e, dopo aver inizializzato due oggetti locali per l’emulazione dei pin dedicati all’input (che verranno sovrascritti ogni volta necessaria), realizza quattro volte la stessa implementazione (poiché, come abbiamo visto nel capitolo 1, il 7400 realizza un NAND logico su 4 diverse coppie di input): • dopo aver verificato che i due oggetti locali di tipo SimulatedPin siano connessi, ne estrapola il livello digitale e va a settare con la negazione dell’AND logico tra essi il rispettivo pin di output simulato; • realizza i passi precedenti anche sulle restanti 3 coppie di pin di input. 23 Sviluppo del codice 68 70 @Override public Pin getVCC () { return getPin (14) ; } 72 74 76 78 80 @Override public Pin getGND () { return getPin (7) ; } @Override public Pin get1A () { return getPin (1) ; } 82 84 86 88 90 @Override public Pin get1B () { return getPin (2) ; } @Override public Pin get1Y () { return getPin (3) ; } 92 94 96 98 100 @Override public Pin get2A () { return getPin (4) ; } @Override public Pin get2B () { return getPin (5) ; } 102 104 106 108 110 @Override public Pin get2Y () { return getPin (6) ; } @Override public Pin get3A () { return getPin (9) ; } 112 114 116 118 120 @Override public Pin get3B () { return getPin (10) ; } @Override public Pin get3Y () { return getPin (8) ; } 122 126 @Override public Pin get4A () { return getPin (12) ; } 128 @Override 124 24 Sviluppo del codice public Pin get4B () { return getPin (13) ; } 130 132 @Override public Pin get4Y () { return getPin (11) ; } 134 136 } Infine abbiamo tutti i metodi di restituzione per ogni specifico pin dell’emulatore. La classe SimulatedTTL7402 Analizziamo la classe che implementa un NAND logico su due valori in input per quattro coppie di valori in ingresso. package com . bergenti . fmachines . simulator . model . ttl ; 2 4 6 8 import import import import import import com . bergenti . fmachines . model . Pin ; com . bergenti . fmachines . model . ttl . TTL7402 ; com . bergenti . fmachines . simulator . EventSink ; com . bergenti . fmachines . simulator . Simulator ; com . bergenti . fmachines . simulator . model . SimulatedChip ; com . bergenti . fmachines . simulator . model . SimulatedPin ; Innanzitutto abbiamo le importazioni necessarie per i modelli di riferimento usati nella creazione/utilizzo di tutti gli elementi emulati e la dichiarazione di appartenenza al pacchetto simulator.model.ttl; 10 public class SimulatedTTL7402 extends SimulatedChip implements TTL7402 , EventSink { public SimulatedTTL7402 ( Simulator simulator , String name ) { super ( simulator , name , Package . DIP , 14) ; 12 for ( int i = 0; i < pins . length ; i ++) { pins [ i ] = new SimulatedPin ( this , name + " . " + PIN_NAMES [ i ] , i + 1) ; } 14 16 } dopodiché possiamo vedere che la nostra classe SimulatedTTL7402 eredita dalla SimulatedChip ed implementa le classi TTL7402 (il modello dell’oggetto) e EventSink, cioè la classe relativa alle modalità di gestione. La classe per l’emulatore non ha campi specifici e quindi ne vediamo direttamente il costruttore; esso • richiama il costruttore della classe base; • crea l’array contente i valori dei corrispondenti pin dell’oggetto fisico. 18 @Override public void initialize () { computeOutputs () ; 25 Sviluppo del codice 20 } 22 @Override public void onLevelChanged ( SimulatedPin pin ) { computeOutputs () ; } 24 Abbiamo poi due funzioni sovrascritte rispetto alla classe base che semplicemente vanno a richiamare il metodo computeOutputs(); la loro implementazione è necessaria in quanto vengono dichiarate all’interno dell’interfaccia TTL7402 implementata dal nostro emulatore. 26 private void computeOutputs () { SimulatedPin a = ( SimulatedPin ) get1A () , b = ( SimulatedPin ) get1B () ; 28 30 if ( a . isConnected () && b . isConnected () ) { boolean by = !( a . getDigitalLevel () || b . getDigitalLevel () ); SimulatedPin y = ( SimulatedPin ) get1Y () ; 32 y . setDigitalLevel ( by ) ; 34 } 36 a = ( SimulatedPin ) get2A () ; 38 b = ( SimulatedPin ) get2B () ; 40 42 if ( a . isConnected () && b . isConnected () ) { boolean by = !( a . getDigitalLevel () || b . getDigitalLevel () ); SimulatedPin y = ( SimulatedPin ) get2Y () ; 44 y . setDigitalLevel ( by ) ; 46 } 48 a = ( SimulatedPin ) get3A () ; 50 b = ( SimulatedPin ) get3B () ; 52 54 if ( a . isConnected () && b . isConnected () ) { boolean by = !( a . getDigitalLevel () || b . getDigitalLevel () ); SimulatedPin y = ( SimulatedPin ) get3Y () ; 56 y . setDigitalLevel ( by ) ; 58 } 60 a = ( SimulatedPin ) get4A () ; 62 b = ( SimulatedPin ) get4B () ; 64 66 68 if ( a . isConnected () && b . isConnected () ) { boolean by = !( a . getDigitalLevel () || b . getDigitalLevel () ); SimulatedPin y = ( SimulatedPin ) get4Y () ; 26 Sviluppo del codice y . setDigitalLevel ( by ) ; 70 } 72 } La funzione computeOutputs() è la funzione centrale dell’emulatore e, dopo aver inizializzato due oggetti locali per l’emulazione dei pin dedicati all’input (che verranno sovrascritti ogni volta necessaria), realizza quattro volte la stessa implementazione (poiché, come abbiamo visto nel capitolo 1, il 7402 realizza un NOR logico su 4 diverse coppie di input): • dopo aver verificato che i due oggetti locali di tipo SimulatedPin siano connessi, ne estrapola il livello digitale e va a settare con la negazione dell’OR logico tra essi il rispettivo pin di output simulato; • realizza i passi precedenti anche sulle restanti 3 coppie di pin di input. 74 76 78 80 @Override public Pin getVCC () { return getPin (14) ; } @Override public Pin getGND () { return getPin (7) ; } 82 84 86 88 90 @Override public Pin get1A () { return getPin (2) ; } @Override public Pin get1B () { return getPin (3) ; } 92 94 96 98 100 @Override public Pin get1Y () { return getPin (1) ; } @Override public Pin get2A () { return getPin (5) ; } 102 104 106 108 110 @Override public Pin get2B () { return getPin (6) ; } @Override public Pin get2Y () { return getPin (4) ; } 112 114 @Override public Pin get3A () { 27 Sviluppo del codice return getPin (8) ; 116 } 118 @Override public Pin get3B () { return getPin (9) ; } 120 122 @Override public Pin get3Y () { return getPin (10) ; } 124 126 @Override public Pin get4A () { return getPin (11) ; } 128 130 132 @Override public Pin get4B () { return getPin (12) ; } 134 136 @Override public Pin get4Y () { return getPin (13) ; } 138 140 142 } Infine abbiamo tutti i metodi di restituzione per ogni specifico pin dell’emulatore. La classe SimulatedTTL7474 Analizziamo la classe che implementa un flip-flop necessario per la realizzazione dei registri. package com . bergenti . fmachines . simulator . model . ttl ; 2 4 6 8 import import import import import import com . bergenti . fmachines . model . Pin ; com . bergenti . fmachines . model . ttl . TTL7474 ; com . bergenti . fmachines . simulator . EventSink ; com . bergenti . fmachines . simulator . Simulator ; com . bergenti . fmachines . simulator . model . SimulatedChip ; com . bergenti . fmachines . simulator . model . SimulatedPin ; Innanzitutto abbiamo le importazioni necessarie per i modelli di riferimento usati nella creazione/utilizzo di tutti gli elementi emulati e la dichiarazione di appartenenza al pacchetto simulator.model.ttl; 10 public class SimulatedTTL7474 extends SimulatedChip implements TTL7474 , EventSink { private boolean state1 = false ; private boolean state2 = false ; 12 14 public SimulatedTTL7474 ( Simulator simulator , String name ) { super ( simulator , name , Package . DIP , 14) ; 28 Sviluppo del codice for ( int i = 0; i < pins . length ; i ++) { pins [ i ] = new SimulatedPin ( this , name + " . " + PIN_NAMES [ i ] , i + 1) ; } 16 18 } dopodiché possiamo vedere che la nostra classe SimulatedTTL74138 eredita dalla SimulatedChip ed implementa le classi TTL74138 (il modello dell’oggetto) e EventSink, cioè la classe relativa alle modalità di gestione. La classe per l’emulatore ha due campi specifici di tipo boolean necessari a mantenere lo stato del clock e il costruttore; esso • richiama il costruttore della classe base; • crea l’array contente i valori dei corrispondenti pin dell’oggetto fisico. 20 22 @Override public void initialize () { computeOutputs () ; } 24 26 28 @Override public void onLevelChanged ( SimulatedPin pin ) { computeOutputs () ; } Abbiamo poi due funzioni sovrascritte rispetto alla classe base che semplicemente vanno a richiamare il metodo computeOutputs(); la loro implementazione è necessaria in quanto vengono dichiarate all’interno dell’interfaccia TTL74138 implementata dal nostro emulatore. 30 private void computeOutputs () { SimulatedPin PR1 = ( SimulatedPin ) getPR1 () , CLR1 = ( SimulatedPin ) getCLR1 () ; SimulatedPin CLK1 = ( SimulatedPin ) getCLK1 () , D1 = ( SimulatedPin ) getD1 () ; 32 if ( PR1 . isConnected () && CLR1 . isConnected () && CLK1 . isConnected () && D1 . isConnected () ) { 34 SimulatedPin Q1 = ( SimulatedPin ) getQ1 () , cQ1 = ( SimulatedPin ) getcQ1 () ; 36 if ( Q1 . isConnected () && cQ1 . isConnected () ) { 38 40 42 44 boolean pr1 = PR1 . getDigitalLevel () , clr1 = CLR1 . getDigitalLevel () ; boolean clk1 = CLK1 . getDigitalLevel () , d1 = D1 . getDigitalLevel () ; if ( pr1 == false && clr1 == true ) { Q1 . setDigitalLevel ( true ) ; cQ1 . setDigitalLevel ( false ) ; } 46 else if ( pr1 == true && clr1 == false ) { 29 Sviluppo del codice Q1 . setDigitalLevel ( false ) ; cQ1 . setDigitalLevel ( true ) ; 48 50 } 52 else if ( pr1 == false && clr1 == false ) { Q1 . setDigitalLevel ( true ) ; cQ1 . setDigitalLevel ( true ) ; } 54 56 else if ( pr1 == true && clr1 == true && d1 == true ) { // transizione verso l ’ alto del clock clk1 if ( clk1 == true && state1 == false ) { Q1 . setDigitalLevel ( true ) ; cQ1 . setDigitalLevel ( false ) ; } } 58 60 62 64 else if ( pr1 == true && clr1 == true && d1 == false ) { // transizione verso l ’ alto del clock clk1 if ( clk1 == true && state1 == false ) { Q1 . setDigitalLevel ( false ) ; cQ1 . setDigitalLevel ( true ) ; } } 66 68 70 72 else if ( pr1 == true && clr1 == true ) { // non transizione verso l ’ alto del clock clk1 if ( clk1 != true && state1 != false ) { // è necessario implementare l ’ hold ? // boolean q01 = Q1 . getDigitalLevel () , cq01 = cQ1 . getDigitalLevel () ; // Q1 . setDigitalLevel ( q01 ) ; // cQ1 . setDigitalLevel ( cq01 ) ; } } 74 76 78 80 82 state1 = clk1 ; } 84 } 86 88 90 SimulatedPin PR2 = ( SimulatedPin ) getPR2 () , CLR2 = ( SimulatedPin ) getCLR2 () ; SimulatedPin CLK2 = ( SimulatedPin ) getCLK2 () , D2 = ( SimulatedPin ) getD2 () ; if ( PR2 . isConnected () && CLR2 . isConnected () && CLK2 . isConnected () && D2 . isConnected () ) { 92 SimulatedPin Q2 = ( SimulatedPin ) getQ2 () , cQ2 = ( SimulatedPin ) getcQ2 () ; 94 if ( Q2 . isConnected () && cQ2 . isConnected () ) { 96 boolean pr2 = PR2 . getDigitalLevel () , clr2 = CLR2 . getDigitalLevel () ; boolean clk2 = CLK2 . getDigitalLevel () , d2 = D2 . getDigitalLevel () ; 98 100 102 if ( pr2 == false && clr2 == true ) { Q2 . setDigitalLevel ( true ) ; cQ2 . setDigitalLevel ( false ) ; } 30 Sviluppo del codice else if ( pr2 == true && clr2 == false ) { Q2 . setDigitalLevel ( false ) ; cQ2 . setDigitalLevel ( true ) ; } 104 106 108 else if ( pr2 == false && clr2 == false ) { Q2 . setDigitalLevel ( true ) ; cQ2 . setDigitalLevel ( true ) ; } 110 112 else if ( pr2 == true && clr2 == true && d2 == true ) { // transizione verso l ’ alto del clock clk2 if ( clk2 == true && state2 == false ) { Q2 . setDigitalLevel ( true ) ; cQ2 . setDigitalLevel ( false ) ; } } 114 116 118 120 else if ( pr2 == true && clr2 == true && d2 == false ) { // transizione verso l ’ alto del clock clk2 if ( clk2 == true && state2 == false ) { Q2 . setDigitalLevel ( false ) ; cQ2 . setDigitalLevel ( true ) ; } } 122 124 126 128 else if ( pr2 == true && clr2 == true && clk2 == false ) { // non transizione verso l ’ alto del clock clk1 if ( clk2 != true && state2 != false ) { // è necessario implementare l ’ hold ? // boolean q02 = Q2 . getDigitalLevel () , cq02 = cQ2 . getDigitalLevel () ; // Q2 . setDigitalLevel ( q02 ) ; // cQ2 . setDigitalLevel ( cq02 ) ; } } 130 132 134 136 138 state2 = clk2 ; 140 } } 142 } La funzione computeOutputs() è la funzione centrale dell’emulatore e realizza due volte la stessa implementazione: • crea e inizializza 4 oggetti di tipo SimulatedPin necessari alla selezione dell’operazione da effettuare, nello specifico i pin di set, reset, clock input e dati in input; • in caso i 4 precedenti siano tutti connessi crea ed inizializza altri due oggetti di tipo SimulatedPin per il settaggio degli output, nello specifico uno la negazione dell’altro; • nel caso anche questi ulteriori 2 siano connessi implementa la tabella in figura 2.4 tramite una serie di controlli condizionali, nello specifico if - else if - else; 31 Sviluppo del codice • aggiorna il valore del campo state con il livello digitale del pin rappresentate il clock. Figura 2.4: Tabella funzionale del TTL 7474 144 146 @Override public Pin getVCC () { return getPin (14) ; } 148 150 152 154 156 @Override public Pin getGND () { return getPin (7) ; } @Override public Pin getCLR1 () { return getPin (1) ; } 158 160 162 164 166 @Override public Pin getD1 () { return getPin (2) ; } @Override public Pin getCLK1 () { return getPin (3) ; } 168 32 Sviluppo del codice @Override public Pin getPR1 () { return getPin (4) ; } 170 172 @Override public Pin getQ1 () { return getPin (5) ; } 174 176 178 @Override public Pin getcQ1 () { return getPin (6) ; } 180 182 @Override public Pin getcQ2 () { return getPin (8) ; } 184 186 188 @Override public Pin getQ2 () { return getPin (9) ; } 190 192 @Override public Pin getPR2 () { return getPin (10) ; } 194 196 198 @Override public Pin getCLK2 () { return getPin (11) ; } 200 202 @Override public Pin getD2 () { return getPin (12) ; } 204 206 208 @Override public Pin getCLR2 () { return getPin (13) ; } 210 212 } Infine abbiamo tutti i metodi di restituzione per ogni specifico pin dell’emulatore. La classe SimulatedTTL74138 Analizziamo la classe che realizza un decoder/demultiplexer con 3 linee in entrata e 8 in uscita. package com . bergenti . fmachines . simulator . model . ttl ; 2 4 import com . bergenti . fmachines . model . Pin ; import com . bergenti . fmachines . model . ttl . TTL74138 ; 33 Sviluppo del codice 6 8 import import import import com . bergenti . fmachines . simulator . EventSink ; com . bergenti . fmachines . simulator . Simulator ; com . bergenti . fmachines . simulator . model . SimulatedChip ; com . bergenti . fmachines . simulator . model . SimulatedPin ; Innanzitutto abbiamo le importazioni necessarie per i modelli di riferimento usati nella creazione/utilizzo di tutti gli elementi emulati e la dichiarazione di appartenenza al pacchetto simulator.model.ttl; 10 public class SimulatedTTL74138 extends SimulatedChip implements TTL74138 , EventSink { public SimulatedTTL74138 ( Simulator simulator , String name ) { super ( simulator , name , Package . DIP , 16) ; 12 for ( int i = 0; i < pins . length ; i ++) { pins [ i ] = new SimulatedPin ( this , name + " . " + PIN_NAMES [ i ] , i + 1) ; } 14 16 } dopodiché possiamo vedere che la nostra classe SimulatedTTL74138 eredita dalla SimulatedChip ed implementa le classi TTL74138 (il modello dell’oggetto) e EventSink, cioè la classe relativa alle modalità di gestione. La classe per l’emulatore non ha campi specifici e quindi ne vediamo direttamente il costruttore; esso • richiama il costruttore della classe base; • crea l’array contente i valori dei corrispondenti pin dell’oggetto fisico. 18 20 22 24 @Override public void initialize () { computeOutputs () ; } @Override public void onLevelChanged ( SimulatedPin pin ) { computeOutputs () ; } Abbiamo poi due funzioni sovrascritte rispetto alla classe base che semplicemente vanno a richiamare il metodo computeOutputs(); la loro implementazione è necessaria in quanto vengono dichiarate all’interno dell’interfaccia TTL74138 implementata dal nostro emulatore. 26 28 30 32 34 36 private void computeOutputs () { SimulatedPin y0 = ( SimulatedPin ) getY0 () ; y0 . setDigitalLevel ( true ) ; SimulatedPin y1 = ( SimulatedPin ) getY1 () ; y1 . setDigitalLevel ( true ) ; SimulatedPin y2 = ( SimulatedPin ) getY2 () ; y2 . setDigitalLevel ( true ) ; SimulatedPin y3 = ( SimulatedPin ) getY3 () ; y3 . setDigitalLevel ( true ) ; SimulatedPin y4 = ( SimulatedPin ) getY4 () ; 34 Sviluppo del codice y4 . setDigitalLevel ( true ) ; SimulatedPin y5 = ( SimulatedPin ) getY5 () ; y5 . setDigitalLevel ( true ) ; SimulatedPin y6 = ( SimulatedPin ) getY6 () ; y6 . setDigitalLevel ( true ) ; SimulatedPin y7 = ( SimulatedPin ) getY7 () ; y7 . setDigitalLevel ( true ) ; 38 40 42 44 SimulatedPin g1 = ( SimulatedPin ) getG1 () , g2a = ( SimulatedPin ) getG2A () , g2b = ( SimulatedPin ) getG2B () ; SimulatedPin a = ( SimulatedPin ) getA () , b = ( SimulatedPin ) getB () , c = ( SimulatedPin ) getC () ; 46 if ( g1 . isConnected () && g2a . isConnected () && g2b . isConnected () ) { if (!( g1 . getDigitalLevel () ) || ( g2a . getDigitalLevel () ) || ( g2b . getDigitalLevel () ) ) { } 48 50 else if ( a . isConnected () && b . isConnected () && c . isConnected () ) { boolean aa = a . getDigitalLevel () ; boolean bb = b . getDigitalLevel () ; boolean cc = c . getDigitalLevel () ; 52 54 56 if ( aa == false && bb == false && cc == false ) { y0 . setDigitalLevel ( false ) ; } 58 60 else if ( aa == false && bb == false && cc == true ) { y1 . setDigitalLevel ( false ) ; } 62 64 else if ( aa == false && bb == true && cc == false ) { y2 . setDigitalLevel ( false ) ; } 66 68 else if ( aa == false && bb == true && cc == true ) { y3 . setDigitalLevel ( false ) ; } 70 72 else if ( aa == true && bb == false && cc == false ) { y4 . setDigitalLevel ( false ) ; } 74 76 else if ( aa == true && bb == false && cc == true ) { y5 . setDigitalLevel ( false ) ; } 78 80 else if ( aa == true && bb == true && cc == false ) { y6 . setDigitalLevel ( false ) ; } 82 84 else { y7 . setDigitalLevel ( false ) ; } 86 } 88 } 90 } La funzione computeOutputs() è la funzione centrale dell’emulatore e: 35 Sviluppo del codice • crea 7 oggetti locali per l’emulazione dei pin dedicati all’output; • inizializza il livello digitale di ciascun oggetto creato a true; • crea e inizializza 3 oggetti di tipo SimulatedPin per le abilitazioni, nello specifico G1, G2A e G2B; • crea e inizializza 3 oggetti di tipo SimulatedPin necessari alla selezione dell’operazione da effettuare, nello specifico A, B e C; • in caso i 3 pin simulati dedicati alle abilitazioni siano connessi ne estrapola il livello digitale; • nel caso in cui anche i 3 pin di selezione siano connessi ne estrapola il livello digitale; • implementa la tabella in figura 2.5 tramite una serie di controlli condizionali, nello specifico if - else if - else andando a settare a false l’opportuno pin di output simulato. Figura 2.5: Tabella funzionale del 74138 92 94 96 98 @Override public Pin getVCC () { return getPin (16) ; } @Override public Pin getGND () { return getPin (8) ; } 36 Sviluppo del codice 100 102 104 106 108 @Override public Pin getA () { return getPin (1) ; } @Override public Pin getB () { return getPin (2) ; } 110 112 114 116 118 @Override public Pin getC () { return getPin (3) ; } @Override public Pin getG2A () { return getPin (4) ; } 120 122 124 126 128 @Override public Pin getG2B () { return getPin (5) ; } @Override public Pin getG1 () { return getPin (6) ; } 130 132 134 136 138 @Override public Pin getY7 () { return getPin (7) ; } @Override public Pin getY6 () { return getPin (9) ; } 140 142 144 146 148 @Override public Pin getY5 () { return getPin (10) ; } @Override public Pin getY4 () { return getPin (11) ; } 150 152 154 156 158 @Override public Pin getY3 () { return getPin (12) ; } @Override public Pin getY2 () { return getPin (13) ; } 160 @Override 37 Sviluppo del codice public Pin getY1 () { return getPin (14) ; } 162 164 @Override public Pin getY0 () { return getPin (15) ; } 166 168 170 } Infine abbiamo tutti i metodi di restituzione per ogni specifico pin dell’emulatore. La classe SimulatedTTL74181 Analizziamo la classe che implementa l’ALU, cuore dell nostro emulatore. package com . bergenti . fmachines . simulator . model . ttl ; 2 4 6 8 import import import import import import com . bergenti . fmachines . model . Pin ; com . bergenti . fmachines . model . ttl . TTL74181 ; com . bergenti . fmachines . simulator . EventSink ; com . bergenti . fmachines . simulator . Simulator ; com . bergenti . fmachines . simulator . model . SimulatedChip ; com . bergenti . fmachines . simulator . model . SimulatedPin ; Innanzitutto abbiamo le importazioni necessarie per i modelli di riferimento usati nella creazione/utilizzo di tutti gli elementi emulati e la dichiarazione di appartenenza al pacchetto simulator.model.ttl; 10 public class SimulatedTTL74181 extends SimulatedChip implements TTL74181 , EventSink { public SimulatedTTL74181 ( Simulator simulator , String name ) { super ( simulator , name , Package . DIP , 24) ; 12 for ( int i = 0; i < pins . length ; i ++) { pins [ i ] = new SimulatedPin ( this , name + " . " + PIN_NAMES [ i ] , i + 1) ; } 14 16 } dopodiché possiamo vedere che la nostra classe SimulatedTTL74138 eredita dalla SimulatedChip ed implementa le classi TTL74181 (il modello dell’oggetto) e EventSink, cioè la classe relativa alle modalità di gestione. La classe per l’emulatore non ha campi specifici e quindi ne vediamo direttamente il costruttore; esso • richiama il costruttore della classe base; • crea l’array contente i valori dei corrispondenti pin dell’oggetto fisico. 18 @Override public void initialize () { computeOutputs () ; 38 Sviluppo del codice 20 } 22 @Override public void onLevelChanged ( SimulatedPin pin ) { computeOutputs () ; } 24 Abbiamo poi due funzioni sovrascritte rispetto alla classe base che semplicemente vanno a richiamare il metodo computeOutputs(); la loro implementazione è necessaria in quanto vengono dichiarate all’interno dell’interfaccia TTL74181 implementata dal nostro emulatore. 26 28 private void computeOutputs () { SimulatedPin S0 = ( SimulatedPin ) getS0 () , S1 = ( SimulatedPin ) getS1 () ; SimulatedPin S2 = ( SimulatedPin ) getS2 () , S3 = ( SimulatedPin ) getS3 () ; 30 if ( S0 . isConnected () && S1 . isConnected () && S2 . isConnected () && S3 . isConnected () ) { 32 SimulatedPin M = ( SimulatedPin ) getM () , C0 = ( SimulatedPin ) getC0 () ; 34 if ( M . isConnected () && C0 . isConnected () ) { 36 38 40 boolean s0 = S0 . getDigitalLevel () , s1 = S1 . getDigitalLevel () ; boolean s2 = S2 . getDigitalLevel () , s3 = S3 . getDigitalLevel () ; SimulatedPin A0 = ( SimulatedPin ) getA0 () , A1 = ( SimulatedPin ) getA1 () ; SimulatedPin A2 = ( SimulatedPin ) getA2 () , A3 = ( SimulatedPin ) getA3 () ; 42 44 46 SimulatedPin B0 = ( SimulatedPin ) getB0 () , B1 = ( SimulatedPin ) getB1 () ; SimulatedPin B2 = ( SimulatedPin ) getB2 () , B3 = ( SimulatedPin ) getB3 () ; if ( A0 . isConnected () && A1 . isConnected () && A2 . isConnected () && A3 . isConnected () && B0 . isConnected () && B1 . isConnected () && B2 . isConnected () && B3 . isConnected () ) { 48 50 52 boolean a0 = A0 . getDigitalLevel () , a1 = A1 . getDigitalLevel () ; boolean a2 = A2 . getDigitalLevel () , a3 = A3 . getDigitalLevel () ; boolean b0 = B0 . getDigitalLevel () , b1 = B1 . getDigitalLevel () ; boolean b2 = B2 . getDigitalLevel () , b3 = B3 . getDigitalLevel () ; 54 56 SimulatedPin F0 = ( SimulatedPin ) getF0 () , F1 = ( SimulatedPin ) getF1 () ; SimulatedPin F2 = ( SimulatedPin ) getF2 () , F3 = ( SimulatedPin ) getF3 () ; 39 Sviluppo del codice 58 SimulatedPin CNp4 = ( SimulatedPin ) getCNp4 () ; 60 if ( F0 . isConnected () && F1 . isConnected () && F2 . isConnected () && F3 . isConnected () ) { // M = high if ( M . getDigitalLevel () ) { setLogical ( s0 , s1 , s2 , s3 , F0 , F1 , F2 , F3 , a0 , a1 , a2 , a3 , b0 , b1 , b2 , b3 ) ; 62 64 } 66 // M = L ( se ho riporto aggiungo 1 all ’ operazione nel commento della setAritmethical ) else { if ( C0 . isConnected () ) { // imposta il riporto a 0 o 1 in base all ’ opportuno pin ( C0 ) int carry = C0 . getDigitalLevel () ? 1 : 0; 68 70 72 int A = setInput ( a0 , a1 , a2 , a3 ) ; int B = setInput ( b0 , b1 , b2 , b3 ) ; 74 setAritmethical ( s0 , s1 , s2 , s3 , F0 , F1 , F2 , F3 , A , B , carry , CNp4 ) ; 76 } 78 } } 80 } } 82 } 84 } La funzione computeOutputs() è la funzione centrale dell’emulatore e realizza due volte la stessa implementazione: • crea e inizializza 4 oggetti di tipo SimulatedPin necessari alla selezione dell’operazione da effettuare; • in caso i 4 precedenti siano tutti connessi crea ed inizializza altri due oggetti di tipo SimulatedPin uno sempre necessario per selezione e uno dedicato alla gestione di eventuale riporto, rispettivamente M e C0; • nel caso anche questi ulteriori 2 siano connessi estrapola i livelli digitali dei primi 4 pin di selezione; • crea e inizializza 8 oggetti di tipo SimulatedPin che identificano gli input da elaborare e ne estrae il livello digitale; • crea e inizializza 4 oggetti di tipo SimulatedPin che identificano gli output da settare; • crea e inizializza un ulteriore oggetto di tipo SimulatedPin per la gestione/propagazione di eventuali riporti; 40 Sviluppo del codice • implementa la tabella in figura 2.6 tramite un controllo condizionale, nello specifico if - else, andando a richiamare la più opportuna tra la funzione setLogical() e la setAritmethical(), più eventualmente il metodo setInput(). public int setInput ( boolean i0 , boolean i1 , boolean i2 , boolean i3 ) { 86 int tmp = i0 ? 1 : 0; int I = tmp ; 88 tmp = i1 ? 1 : 0; I = I + tmp * 2; 90 92 tmp = i2 ? 1 : 0; I = I + tmp * 4; 94 tmp = i3 ? 1 : 0; I = I + tmp * 8; 96 98 return I ; 100 } Abbiamo poi la funzione setInput che, presi 4 valori di tipo boolean in input, costruisce un intero con i parametri per svolgere le operazioni aritmetiche; 102 public void setOutput ( int F , SimulatedPin F0 , SimulatedPin F1 , SimulatedPin F2 , SimulatedPin F3 , SimulatedPin CNp4 ) { 104 106 if ( F != -1) { boolean tmp = ( F % 2) > 0 ? true : false ; 108 F0 . setDigitalLevel ( tmp ) ; 110 F = F / 2; tmp = ( F % 2) > 0 ? true : false ; F1 . setDigitalLevel ( tmp ) ; 112 F = F / 2; tmp = ( F % 2) > 0 ? true : false ; F2 . setDigitalLevel ( tmp ) ; 114 116 F = F / 2; tmp = ( F % 2) > 0 ? true : false ; F3 . setDigitalLevel ( tmp ) ; 118 120 if ( F > 0) { CNp4 . setDigitalLevel ( true ) ; } 122 124 } 126 128 130 else { F0 . setDigitalLevel ( true ) ; F1 . setDigitalLevel ( false ) ; F2 . setDigitalLevel ( false ) ; F3 . setDigitalLevel ( true ) ; 41 Sviluppo del codice } 132 134 } mentre, in maniera complementare a prima, la funzione setOutput(), presi in ingresso: • un intero contente il risultato delle operazioni eseguite; • 4 oggetti di tipo SimulatedPin rappresentanti i pin da settare con il primo parametro in input alla funzione; • un ulteriore oggetti SimulatedPin per l’eventuale gestione/propagazione del riporto derivato dalle operazioni svolte; ripartisce i 5 bit per l’output dall’intero calcolato tramite operazioni aritmetiche. 136 138 140 142 144 public void setLogical ( boolean s0 , boolean s1 , boolean s2 , boolean s3 , SimulatedPin F0 , SimulatedPin F1 , SimulatedPin F2 , SimulatedPin F3 , boolean a0 , boolean a1 , boolean a2 , boolean a3 , boolean b0 , boolean b1 , boolean b2 , boolean b3 ) { if ( s3 == false && s2 == false && s1 == false && s0 == false ) { // F = not A F0 . setDigitalLevel (! a0 ) ; F1 . setDigitalLevel (! a1 ) ; F2 . setDigitalLevel (! a2 ) ; F3 . setDigitalLevel (! a3 ) ; 146 } 148 150 152 154 else if ( s3 == false && s2 true ) { // F = not ( A + B ) F0 . setDigitalLevel (!( a0 F1 . setDigitalLevel (!( a1 F2 . setDigitalLevel (!( a2 F3 . setDigitalLevel (!( a3 } == false && s1 == false && s0 == || || || || b0 ) ) ; b1 ) ) ; b2 ) ) ; b3 ) ) ; 156 158 160 162 else if ( s3 == false && s2 == false && s1 == true && s0 == false ) { // F = not ( A ) B F0 . setDigitalLevel ((! a0 ) && b0 ) ; F1 . setDigitalLevel ((! a1 ) && b1 ) ; F2 . setDigitalLevel ((! a2 ) && b2 ) ; F3 . setDigitalLevel ((! a3 ) && b3 ) ; } 164 166 168 else if ( s3 == false && s2 == false && s1 == true && s0 == true ) { // F = false ( logical zero ) F0 . setDigitalLevel ( false ) ; F1 . setDigitalLevel ( false ) ; F2 . setDigitalLevel ( false ) ; 42 Sviluppo del codice F3 . setDigitalLevel ( false ) ; 170 } 172 174 176 178 else if ( s3 == false && s2 false ) { // F = not ( AB ) F0 . setDigitalLevel (!( a0 F1 . setDigitalLevel (!( a1 F2 . setDigitalLevel (!( a2 F3 . setDigitalLevel (!( a3 } == true && s1 == false && s0 == && && && && b0 ) ) ; b1 ) ) ; b2 ) ) ; b3 ) ) ; 180 182 184 186 else if ( s3 == false && s2 == true && s1 == false && s0 == true ) { // F = not B F0 . setDigitalLevel (! b0 ) ; F1 . setDigitalLevel (! b1 ) ; F2 . setDigitalLevel (! b2 ) ; F3 . setDigitalLevel (! b3 ) ; } 188 190 192 194 else if ( s3 == false && s2 false ) { // F = A XOR B F0 . setDigitalLevel ( a0 ^ F1 . setDigitalLevel ( a1 ^ F2 . setDigitalLevel ( a2 ^ F3 . setDigitalLevel ( a3 ^ } == true && s1 == true && s0 == b0 ) ; b1 ) ; b2 ) ; b3 ) ; 196 198 200 202 else if ( s3 == false && s2 == true && s1 == true && s0 == true ) { // F = Anot ( B ) F0 . setDigitalLevel ( a0 && (! b0 ) ) ; F1 . setDigitalLevel ( a1 && (! b1 ) ) ; F2 . setDigitalLevel ( a2 && (! b2 ) ) ; F3 . setDigitalLevel ( a3 && (! b3 ) ) ; } 204 206 208 210 else if ( s3 == true && s2 == false && s1 == false && s0 == false ) { // F = not ( A ) + B F0 . setDigitalLevel ((! a0 ) || b0 ) ; F1 . setDigitalLevel ((! a1 ) || b1 ) ; F2 . setDigitalLevel ((! a2 ) || b2 ) ; F3 . setDigitalLevel ((! a3 ) || b3 ) ; } 212 214 216 218 else if ( s3 == true && s2 == true ) { // F = not ( A XOR B ) F0 . setDigitalLevel (!( a0 ^ F1 . setDigitalLevel (!( a1 ^ F2 . setDigitalLevel (!( a2 ^ F3 . setDigitalLevel (!( a3 ^ } false && s1 == false && s0 == b0 ) ) ; b1 ) ) ; b2 ) ) ; b3 ) ) ; 220 222 224 else if ( s3 == true && s2 == false && s1 == true && s0 == false ) { // F = B F0 . setDigitalLevel ( b0 ) ; F1 . setDigitalLevel ( b1 ) ; 43 Sviluppo del codice F2 . setDigitalLevel ( b2 ) ; F3 . setDigitalLevel ( b3 ) ; 226 } 228 else if ( s3 == true && s2 == false && s1 == true && s0 == true ) { // F = AB F0 . setDigitalLevel ( a0 && b0 ) ; F1 . setDigitalLevel ( a1 && b1 ) ; F2 . setDigitalLevel ( a2 && b2 ) ; F3 . setDigitalLevel ( a3 && b3 ) ; } 230 232 234 236 else if ( s3 == true && s2 == true && s1 == false && s0 == false ) { // F = true ( logical 1) F0 . setDigitalLevel ( true ) ; F1 . setDigitalLevel ( true ) ; F2 . setDigitalLevel ( true ) ; F3 . setDigitalLevel ( true ) ; } 238 240 242 244 else if ( s3 == true && s2 == true && s1 == false && s0 == true ) { // F = A + not ( B ) F0 . setDigitalLevel ( a0 || (! b0 ) ) ; F1 . setDigitalLevel ( a1 || (! b1 ) ) ; F2 . setDigitalLevel ( a2 || (! b2 ) ) ; F3 . setDigitalLevel ( a3 || (! b3 ) ) ; } 246 248 250 252 else if ( s3 == true && s2 == true && s1 == true && s0 == false ) { // F = A + B F0 . setDigitalLevel ( a0 || b0 ) ; F1 . setDigitalLevel ( a1 || b1 ) ; F2 . setDigitalLevel ( a2 || b2 ) ; F3 . setDigitalLevel ( a3 || b3 ) ; } 254 256 258 260 else { // F = A F0 . setDigitalLevel ( a0 ) ; F1 . setDigitalLevel ( a1 ) ; F2 . setDigitalLevel ( a2 ) ; F3 . setDigitalLevel ( a3 ) ; } 262 264 266 268 } Vediamo ora la funzione setLogical(), fondamentale per il funzionamento del metodo computeOutputs(); essa • prende in input i 4 bit di selezione dell’operazione, i 4 pin simulati per l’output, i 4 bit per la prima variabile in input e i 4 bit per la seconda variabile in input; • setta i valori di output ottenuti tramite operazioni logiche secondo la tabella in figura 2.6 tramite una serie di controlli condizionali, nello specifico if - else if - else. 44 Sviluppo del codice 270 public void setAritmethical ( boolean s0 , boolean s1 , boolean s2 , boolean s3 , SimulatedPin F0 , SimulatedPin F1 , SimulatedPin F2 , SimulatedPin F3 , int A , int B , int carry , SimulatedPin CNp4 ) { 272 276 if ( s0 == false && s1 == false && s2 == false && s3 == false ) { // F = A int F = A + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 278 } 280 else if ( s0 == false && s1 == false && s2 == false && s3 == true ) { // F = A + B int F = ( A ^ B ) + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 274 282 284 } 286 290 else if ( s0 == false && s1 == false && s2 == true && s3 == false ) { // F = A + not ( B ) int F = ( A ^ (~ B ) ) + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 292 } 294 else if ( s0 == false && s1 == false && s2 == true && s3 == true ) { // F = minus 1 (2 s Comp ) int F = ( -1) + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 288 296 298 } 300 304 else if ( s0 == false && s1 == true && s2 == false && s3 == false ) { // F = A plus ( A *( notB ) ) int F = A + ( A & (~ B ) ) + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 306 } 308 else if ( s0 == false && s1 == true && s2 == false && s3 == true ) { // F = ( A + B ) plus A *( notB ) int F = ( A ^ B ) + ( A & (~ B ) ) + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 302 310 312 } 314 318 else if ( s0 == false && s1 == true && s2 == true && s3 == false ) { // F = A minus B minus 1 int F = A - B - 1 + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 320 } 316 45 Sviluppo del codice 322 324 else if ( s0 == false && s1 == true && s2 == true && s3 == true ) { // F = A *( notB ) minus 1 int F = ( A & (~ B ) ) - 1 + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 326 } 328 332 else if ( s0 == true && s1 == false && s2 == false && s3 == false ) { // F = A plus A * B int F = A + ( A & B ) + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 334 } 336 else if ( s0 == true && s1 == false && s2 == false && s3 == true ) { // F = A plus B int F = A + B + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 330 338 340 } 342 346 else if ( s0 == true && s1 == false && s2 == true && s3 == false ) { // F = ( A + not ( B ) ) plus A * B int F = ( A ^ (~ B ) ) + ( A & B ) + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 348 } 350 else if ( s0 == true && s1 == false && s2 == true && s3 == true ) { // F = A * B minus 1 int F = ( A & B ) - 1 + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 344 352 354 } 356 360 else if ( s0 == true && s1 == true && s2 == false && s3 == false ) { // F = A plus A * ( each bit is shifted to the next more significant position ) int F = A + A + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 362 } 364 else if ( s0 == true && s1 == true && s2 == false && s3 == true ) { // F = ( A + B ) plus A int F = ( A ^ B ) + A + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 358 366 368 } 370 372 else if ( s0 == true && s1 == true && s2 == true && s3 == false ) { // F = ( A + not ( B ) ) plus A int F = ( A ^ (~ B ) ) + A + carry ; 46 Sviluppo del codice setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 374 376 } 378 else { // F = A minus 1 int F = A - 1 + carry ; setOutput (F , F0 , F1 , F2 , F3 , CNp4 ) ; 380 382 } 384 } Continuiamo con la funzione setAritmethical(), anch’essa fondamentale per il funzionamento del metodo computeOutputs(); la quale • prende in input i valori dei 4 bit di selezione dell’operazione, nello specifico s0, s1, s2 e s3; • i 4 pin simulati per l’output, nello specifico F0, F1, F2 e F3; • un intero rappresentante la prima variabile, nello specifico A; • un intero rappresentante la seconda variabile, nello specifico B; • il valore del riporto in input e il pin simulato per il riporto in output, rispettivamente C0 e CNp4; • setta i valori di output ottenuti tramite operazioni aritmetiche secondo la tabella in figura 2.5 tramite una serie di controlli condizionali, nello specifico if - else if - else. Osservazioni sulla tabella 2.6 • Con + si intende una somma esclusiva bit a bit (che non genera riporto); • con plus una somma aritmetica; • con * un prodotto bit a bit (che non genera riporto); • con minus una sottrazione aritmetica; • con not una negazione bit a bit. Inoltre, nel caso Cn = L (nell’implementazione delle operazioni aritmetiche), sarà sufficiente sottrarre 1 al risultato mostrato in tabella. 386 388 @Override public Pin getVCC () { return getPin (24) ; } 390 @Override 47 Sviluppo del codice Figura 2.6: Tabella funzionale in caso di operazioni aritmetiche per il 74181 392 394 396 398 public Pin getGND () { return getPin (12) ; } @Override public Pin getB0 () { return getPin (1) ; } 400 402 404 406 408 @Override public Pin getA0 () { return getPin (2) ; } @Override public Pin getS3 () { return getPin (3) ; } 410 412 414 @Override public Pin getS2 () { return getPin (4) ; } 48 Sviluppo del codice 416 418 @Override public Pin getS1 () { return getPin (5) ; } 420 422 424 426 428 @Override public Pin getS0 () { return getPin (6) ; } @Override public Pin getC0 () { return getPin (7) ; } 430 432 434 436 438 @Override public Pin getM () { return getPin (8) ; } @Override public Pin getF0 () { return getPin (9) ; } 440 442 444 446 448 @Override public Pin getF1 () { return getPin (10) ; } @Override public Pin getF2 () { return getPin (11) ; } 450 452 454 456 458 @Override public Pin getF3 () { return getPin (12) ; } @Override public Pin getAeqB () { return getPin (14) ; } 460 462 464 466 468 @Override public Pin getP () { return getPin (15) ; } @Override public Pin getCNp4 () { return getPin (16) ; } 470 472 474 476 @Override public Pin getG () { return getPin (17) ; } @Override public Pin getB3 () { 49 Sviluppo del codice return getPin (18) ; 478 } 480 @Override public Pin getA3 () { return getPin (19) ; } 482 484 @Override public Pin getB2 () { return getPin (20) ; } 486 488 490 @Override public Pin getA2 () { return getPin (21) ; } 492 494 @Override public Pin getB1 () { return getPin (22) ; } 496 498 500 @Override public Pin getA1 () { return getPin (23) ; } 502 504 506 } Infine abbiamo tutti i metodi di restituzione per ogni specifico pin dell’emulatore. 50 Sviluppo del codice Comunicazione RS-232 - architettura TTL Figura 2.7: Schema di realizzazione della comunicazione RS-232 - TTL La figura 2.7 rappresenta la strutturazione di base di collegamento tra una porta seriale1 e un MAX2322 . Qui vediamo un MAX232 che realizza ricezione e trasferimenti di dati, collegato ad un connettore RS-232 che invia dati su un cavo seriale (serial cable); in questo modo e grazie all’interazione dei componenti i risultati ottenuti in logica TTL possono essere trasmessi secondo lo standard RS-232. 1 Sezione 2 Sezione di riferimento 1. di riferimento 1. 51 Sviluppo del codice La classe SimulatedMAX232 Analizziamo la classe relativa all’implementazione del convertitore TTL – RS232. package com . bergenti . fmachines . simulator . model . ttl ; 2 4 6 8 import import import import import import com . bergenti . fmachines . model . Pin ; com . bergenti . fmachines . model . ttl . MAX232 ; com . bergenti . fmachines . simulator . EventSink ; com . bergenti . fmachines . simulator . Simulator ; com . bergenti . fmachines . simulator . model . SimulatedChip ; com . bergenti . fmachines . simulator . model . SimulatedPin ; Innanzitutto abbiamo le importazioni necessarie per i modelli di riferimento usati nella creazione/utilizzo di tutti gli elementi emulati e la dichiarazione di appartenenza al pacchetto simulator.model.ttl; 10 public class SimulatedMAX232 extends SimulatedChip implements MAX232 , EventSink { public SimulatedMAX232 ( Simulator simulator , String name ) { super ( simulator , name , Package . DIP , 14) ; 12 for ( int i = 0; i < pins . length ; i ++) { pins [ i ] = new SimulatedPin ( this , name + " . " + PIN_NAMES [ i ] , i + 1) ; } 14 16 } dopodiché possiamo vedere che la nostra classe SimulatedMAX232 eredita dalla SimulatedChip ed estende le classi MAX232 (il modello dell’oggetto) e EventSink, cioè la classe relativa alle modalità di gestione. La classe per l’emulatore non ha campi specifici e quindi ne vediamo direttamente il costruttore; esso • richiama il costruttore della classe base; • crea l’array contente i valori dei corrispondenti pin dell’oggetto fisico. 18 20 22 24 @Override public void initialize () { computeOutputs () ; } @Override public void onLevelChanged ( SimulatedPin pin ) { computeOutputs () ; } Abbiamo poi due funzioni sovrascritte rispetto alla classe base che semplicemente vanno a richiamare il metodo computeOutputs(); la loro implementazione è necessaria in quanto vengono dichiarate all’interno dell’interfaccia MAX232 implementata dal nostro emulatore. 52 Sviluppo del codice 26 private void computeOutputs () { SimulatedPin T1IN = ( SimulatedPin ) getT1IN () , T2IN = ( SimulatedPin ) getT2IN () ; 28 if ( T1IN . isConnected () ) { boolean t1in = T1IN . getDigitalLevel () ; 30 SimulatedPin T1OUT = ( SimulatedPin ) getT1OUT () ; 32 T1OUT . setDigitalLevel (! t1in ) ; 34 } 36 if ( T2IN . isConnected () ) { boolean t2in = T2IN . getDigitalLevel () ; 38 SimulatedPin T2OUT = ( SimulatedPin ) getT2OUT () ; 40 T2OUT . setDigitalLevel (! t2in ) ; 42 } 44 SimulatedPin R1IN = ( SimulatedPin ) getR1IN () , R2IN = ( SimulatedPin ) getR2IN () ; 46 if ( R1IN . isConnected () ) { boolean r1in = R1IN . getDigitalLevel () ; 48 SimulatedPin R1OUT = ( SimulatedPin ) getR1OUT () ; 50 R1OUT . setDigitalLevel (! r1in ) ; 52 } 54 if ( R2IN . isConnected () ) { boolean r2in = R2IN . getDigitalLevel () ; 56 SimulatedPin R2OUT = ( SimulatedPin ) getR2OUT () ; 58 R2OUT . setDigitalLevel (! r2in ) ; 60 } 62 } La funzione computeOutputs() è la funzione centrale dell’emulatore e realizza due volte la stessa implementazione (poiché, come abbiamo visto nel capitolo 1, il MAX232 realizza due canali di comunicazione): • crea i simulatori per i due pin dedicati all’input; • dopo aver verificato che il primo sia connesso, ne estrapola il livello digitale e va a settare con la negazione logica di esso il rispettivo pin di output simulato; • realizza il passo precedente anche sul secondo pin di input. 66 public boolean returnOutput1 () { SimulatedPin T1OUT = ( SimulatedPin ) getT1OUT () ; return T1OUT . getDigitalLevel () ; } 68 public boolean returnOutput2 () { 64 53 Sviluppo del codice SimulatedPin T2OUT = ( SimulatedPin ) getT2OUT () ; return T2OUT . getDigitalLevel () ; 70 } Le due funzioni che vediamo qui sopra possono essere trattate insieme in quanto, di fatto, gemelle; esse semplicemente restituiscono gli output da inviare. Risulta necessaria una precisazione, dal punto di vista logico dovrebbero restituire il dato in RS-232, ma dal punto di vista funzionale del nostro progetto ci sono sufficienti due oggetti di tipo boolean in quanto la nostra porta seriale (a cui collegare il MAX232) sarà trasparente a livello di codice. 72 74 @Override public Pin getVCC () { return getPin (16) ; } 76 78 80 82 84 @Override public Pin getGND () { return getPin (15) ; } @Override public Pin getC1p () { return getPin (1) ; } 86 88 90 92 94 @Override public Pin getVsp () { return getPin (2) ; } @Override public Pin getC1n () { return getPin (3) ; } 96 98 100 102 104 @Override public Pin getC2p () { return getPin (4) ; } @Override public Pin getC2n () { return getPin (5) ; } 106 108 110 112 114 @Override public Pin getVsn () { return getPin (6) ; } @Override public Pin getT2OUT () { return getPin (7) ; } 116 @Override 54 Sviluppo del codice 118 120 122 124 public Pin getR2IN () { return getPin (8) ; } @Override public Pin getR2OUT () { return getPin (9) ; } 126 128 130 132 134 @Override public Pin getT2IN () { return getPin (10) ; } @Override public Pin getT1IN () { return getPin (11) ; } 136 138 140 142 144 @Override public Pin getR1OUT () { return getPin (12) ; } @Override public Pin getR1IN () { return getPin (13) ; } 146 148 150 @Override public Pin getT1OUT () { return getPin (14) ; } Infine abbiamo tutti i metodi di restituzione per ogni specifico pin dell’emulatore. 55 Conclusioni e sviluppi futuri Il presente progetto di tesi mira a fornire un’impostazione gerarchica per la realizzazione di un emulatore hardware di componenti TTL di tipo funzionale per architetture complesse secondo il modello di Eckert. Dopo una prima breve introduzione sulla tecnologia Transistor-Transistor Logic, la serie di componenti 74xx utilizzata (introdotta sul mercato dalla Texas Instruments a metà degli anni ’60) e la descrizione del funzionamento di una CPU di Eckert; si passa ad un’analisi più approfondita degli specifici componenti utilizzati approfondendone la struttura e il comportamento logico per poi presentare una strutturazione funzionale che li utilizzi per la realizzazione di un’architettura complessa. La strutturazione per l’emulazione può essere vista logicamente suddivisa; partendo dalla creazione, il collegamento e la gestione dei contatti interni, si può poi prendere in esame la realizzazione e il management del segnale di clock, per poi analizzare successivamente la costruzione dei componenti specifici e le loro funzionalità; per terminare, si ha la realizzazione di un profilo comunicativo tra due oggetti che utilizzano differenti standard logici. Nello specifico i componenti realizzati a livello di codice sono stati: • il 7400 che realizza un NAND logico su quattro coppie di bit in input con quattro bit di output (uno per ciascuna coppia di valori); • il 7402 per ottenere un NOR logico anch’esso tra quattro coppie di bit in input con quattro bit di output (uno per ciascuna coppia di valori); • il 7474, ovvero la struttura di un FLIP-FLOP da riprodurre più volte per l’implementazione dei registri; • il 74138 che implementa un decoder/DEMULTIPLEXER nella versione con tre linee in ingresso e 8 linee in uscita; • il 74181 per l’Aritmethic e Logic Unit; • il MAX232 che è un convertitore da logica TTL a RS-232 e vieceversa. 56 Sviluppo del codice A livello di possibili sviluppi futuri del progetto, si è pensato all’implementazione di un bus di collegamento e alla realizzazione di due ulteriori emulatori per oggetti della serie 74xx: • il 7488 che realizza una ROM da 256 bit; • il 74189 che implementa una RAM da 64 bit. L’introduzione di questo ulteriore sviluppo renderebbe possibile il collegamento di tutti i componenti e la realizzazione di un’architettura perfettamente emulabile dal punto di vista hardware. Vista la semplicità e la potenza dell’architettura di Eckert, che la rende un utile strumento didattico, già vari istituti scolastici si sono dichiarati interessati all’emulatore oggetto di questo lavoro di tesi come strumento di supporto all’insegnamento dei principi della progettazione degli elaboratori. 57 Bibliografia [1] J Presper Eckert Jr. A survey of digital computer memory systems. Proceedings of the IRE, 41(10):1393–1406, 1953. [2] Datasheet 74F00 Quad 2-input NAND gate. http://www.nxp.com/ documents/data_sheet/74F00.pdf, 1990. [3] Datasheet 74F02 Quad 2-input NOR gate. documents/data_sheet/74F02.pdf, 1990. http://www.nxp.com/ [4] Datasheet 74F74 Dual D-type flip-flop. documents/data_sheet/74F74.pdf, 1996. http://www.nxp.com/ [5] Datasheet 74HC/HCT138 3-to-8 line decoder/demultiplexer. http:// www.nxp.com/documents/data_sheet/74HC_HCT138.pdf, 2012. [6] Datasheet 74HC/HCT181 4-bit arithmetic logic unit. http: //www.ic.unicamp.br/~cortes/mc613/material_complementar/ 74HC_HCT181_Philips.pdf, 1998. [7] Datasheet MAX232 Dual EIA-232 Drivers/Receivers. http://www.ti. com/lit/ds/symlink/max232.pdf, 1989. 58