Progettazione e realizzazione di un emulatore funzionale di

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