Calcolatori Elettronici T

Transcript

Calcolatori Elettronici T
Calcolatori Elettronici T
Input/Ouput
1
Il sottosistema di I/O
•  Il sottosistema di I/O consente la comunicazione fra il calcolatore ed il mondo
esterno. Fanno parte del sottosistema i dispositivi (Unità di I/O) per la comunicazione
uomo/macchina (video, terminali, stampanti), quelli utilizzati per la memorizzazione
permanente delle informazioni (unità a disco, nastri magnetici), la rete.
•  Tutte le Unità di I/O sono collegate al bus di sistema mediante dispositivi detti
Interfacce di I/O (o anche Periferiche, Controllori di I/O, Porte di I/O)
MEMORIA
PRINCIPALE
CPU
UNITA’ DI I/O
UNITA’ DI I/O
INTERFACCIA
DI I/O
INTERFACCIA
DI I/O
Bus dati
Bus indirizzi
Bus comandi
2
I/O BASE
VCC
IN
GROUND
VCC
220V
µP
OUT
3
I/O solo 16
bit significativi
I/O BASE
BADRi
8
0
8
8
D
E
C
O
D
16
I/O !!
CE*
IORD*
Nel lucido successivo si ipotizza
indirizzo 28h
CE = A5* A4!* A3 decodif. sempl.
read_io*
oe*
74XX244
IN
ADi
VCC
D
IOWR*
CL
RESET*
CE*
T1
PR
T2
T3
Q
Q*
OUT
alla base del transistor
74XX74
T4
IORD*
IOWR*
4
Interfacce di I/O
Bus
Bus
comandi indirizzi
Bus
dati
D[0:7]
CS*
RD*
WR*
INTERFACCIA
DI I/O
Segnali
UNITA’ DI I/O
(8 bit )
A[0:n-1]
Interfaccia standard
verso il bus di sistema
Interfaccia specifica (dipendente dal
tipo di unità) verso l’unità di I/O
•  L’interfaccia svolge una funzione di adattamento fra la modalità di trasferimento
dei dati utilizzata all’interno del sistema (cicli di bus) e quella utilizzata dall’Unità
di I/O.
5
Interfacce di I/O
•  Buffer Dati
Strettamente associata alla funzionalità di adattamento fra calcolatore ed unità di I/O
è la presenza all’interno dell’interfaccia di registri di appoggio (“buffer”) utilizzati
nei trasferimenti dei dati da CPU ad Unità di I/O e viceversa.
Per inviare un dato all’Unità di I/O la CPU effettua una scrittura del dato su un buffer
dell’interfaccia, da cui poi quest’ultima si occupa di trasferire il dato all’Unità di I/O.
Per prelevare un dato dall’Unità di I/O la CPU effettua una lettura da un buffer
dell’interfaccia, su cui quest’ultima ha precedentemente appoggiato il dato proveniente
dall’Unità di I/O (può essere anche connessa via tri-state).
•  Programmazione e Registri di Controllo
Le interfacce di I/O devono poter funzionare secondo un certo insieme di modalità
differenti (es.: un’interfaccia per comunicazioni deve poter scambiare dati
impiegando trame e frequenze differenti). Conseguentemente le interfacce di I/O
sono tipicamente programmabili: sono presenti cioè al loro interno un certo numero
di registri di controllo che vengono scritti dalla CPU all’atto dell’inizializzazione del
dispositivo per impostare la modalità di funzionamento desiderata.
6
Struttura di una generica interfaccia di I/O
Bus
Bus dati
indirizzi
OE* BufferIn
Bus
comandi
CK
CK BufferOut OE*
BD[0:7]
Segnali
D[0:7]
OE*
Status
CK
UNITA’
DI I/O
CK
CSPER*
CS*
BA0
BA1
A0
A1
INTERFACCIA
DI I/O
• 
RD*
WR*
• 
IORDC*
IOWRC*
0
1
2
EN* DEC
Control OE*
3
A B
(8 bit, CPU con bus dati
ad 8 bit, 4 registri
interni, I/O mapped)
7
Interfaccia delle periferiche
•  Le periferiche hanno dei registri interni accessibili alla stessa stregua di
come si accede alle celle di memoria .
•  E’ necessario fare attenzione alla decodifica semplificata
•  Esempio: due periferiche Per-1 e Per-2 ciascuna dotata di quattro registri
interni A,B,C e D -> 4 indirizzi nello spazio di indirizzamento di I/O
Decodifica semplificata
CSPer-1 = A7 CSPer-2 = A6 errata !!!!!
A7 = A7 · (A6 + A6*) e quindi possibile sovrapposizione !!!!!
Chi scrive il software potrebbe usare l’indirizzo A7*A6 per la periferica 1 e in
tal caso attiverebbe contemporaneamente anche la periferica 2 !!!!!!!!!
CSPer-1 = A7* CSPer-2 = A7 corretta!!!!!
8
Periferiche su bus a 16 bit
•  Le periferiche a 8 bit possono risiedere sul bus basso o sul bus alto
•  Se collegato al bus basso utilizzano solo indirizzi pari, se collegate sul bus alto
utilizzano solo indirizzi dispari. Esempio: due periferiche distinte allocate
rispettivamente agli indirizzi 78H (bus basso) e 81 (bus alto) contenti al loro
interno 4 registri di lettura o scrittura, ciascuna con quattro registri A,B,C e D
Per-2
D08-D15
Proc.
16 bit
D00-D07
Per-1
Registri
Per-1
Per-2
A
78
81
B
7A
83
C
7C
85
D
7E
87
BLE* = A0
BHE*
Ogni dispositivo occupa uno spazio di indirizzamento doppio del numero dei suoi
indirizzi interni
9
Come nel caso delle memorie il CS deve essere condizionato da A0 e BHE*
Esempio
Mem
D08-D15
Proc.
16 bit
245-1
D00-D07
245-2
Mem
Per
E’ possibile collocare le periferiche su un bus a parte. In questo caso la periferica
risponde a tutti gli indirizzi e i dati sono trasferiti alternativamente sul bus basso e
su quello alto
Il segnale DIR dei 245 dipende
Registri A
B
C
D
ovviamente dal senso
Perif
78
79
7A 7B
dell’accesso indicato dal
Bus Basso Alto Basso Alto processore (DT/R* nel caso
EN-245 245-2 245-1 245-2 245-1 dell’8086)
10
Accesso ai registri interni dell’interfaccia
Hyp. (1) : CSPER = A15⋅A14⋅A13⋅A12⋅A11⋅A10⋅A9⋅A8⋅A7⋅A6⋅A5⋅A4⋅A3⋅A2 = A7
Nel caso bus a 8 bit la CPU vede i 4 registri interni dell’interfaccia agli indirizzi:
BufferIn: 80H (128), BufferOut: 81H (129), Status: 82H (130), Control: 83H (131)
Hyp. (2) : La configurazione binaria (Control Word) che è necessario scrivere
nel registro di controllo per far si che l’interfaccia funzioni nel modo
desiderato è 11010100 (D4H).
Un programmatore che deve scrivere un programma Assembler (IA16) che ha la
necessità di accedere ai registri dell’interfaccia utilizzerà (tipicamente) la direttiva
EQU (definizione costanti in ASM 86) per definire degli identificatori associati alle
costanti che rappresentano gli indirizzi dei registri e la Control Word:
BufferIn
BufferOut
StatusRegister
ControlRegister
ControlWord
EQU 80H
EQU 81H
EQU 82H
EQU 83H
EQU D4H
11
Accesso ai registri interni dell’interfaccia
•  Programmazione dell’interfaccia
Nella porzione di programma che si occupa di inizializzare tutte le periferiche
programmabili presenti nel sistema il programmatore inserirà le seguenti istruzioni:
MOV AL, ControlWord
OUT ControlRegister, AL
; caricamento in AL della costante “ControlWord”
; trasferimento del contenuto di AL nel registro
; mappato all’indirizzo “ControlRegister” dello
; spazio di I/O
•  Lettura dello stato
Ogni qual volta il programma ha la necessità di leggere lo stato della periferica (ad
esempio per controllare se si sono verificati degli errori oppure, come vedremo meglio in
seguito, per sincronizzare il programma con l’Unità di I/O connessa al sistema tramite
l’interfaccia) il programmatore inserirà la seguente istruzione:
IN AL, StatusRegister
; caricamento in AL del contenuto del registro
; mappato all’indirizzo “StatusRegister”
; dello spazio di I/O
12
Accesso ai registri interni dell’interfaccia
•  Trasferimento di dati (Input)
Supponiamo che il programma debba ricevere una sequenza di dati dall’Unità di I/O e
memorizzarli nel vettore DatiIn (Operazione di Input). Supponiamo inoltre che il
programma sia opportunamente sincronizzato con l’Unità di I/O (il programma va a
leggere un dato dall’interfaccia quando questo è stato effettivamente reso disponibile in
BufferIn dall’Unità di I/O). In queste ipotesi il programmatore inserirà le seguenti
istruzioni al fine di prelevare l’ i-esimo dato della sequenza:
IN AL, BufferIn
MOV DatiIn[SI], AL
INC SI
; trasferimento in AL del contenuto del registro
; mappato all’indirizzo “BufferIn dello spazio di I/O
; trasferimento del contenuto di AL nella cella di
; indice SI del vettore DatiIn. Se il programma sta
; prelevando l’i-esimo dato SI, che è stato
; opportunamente inizializzato, contiene il valore i-1
; incremento dell’indice del vettore, che così punta
; alla cella di memoria in cui deve essere inserito
; il prossimo dato prelevato dall’interfaccia
13
Accesso ai registri interni dell’interfaccia
•  Trasferimento di dati (Output)
Supponiamo che il programma debba inviare una sequenza di dati memorizzati nel
vettore DatiOut all’Unità di I/O (Operazione di Output). Supponiamo inoltre che il
rogramma sia opportunamente sincronizzato con l’Unità di I/O (il programma va a
scrivere un dato sul registro BufferOut dall’interfaccia quando questo registro è
“vuoto”, cioè quando l’Unità di I/O ha ricevuto il dato inviato precedentemente). In
queste ipotesi il programmatore inserirà le seguenti istruzioni al fine di inviare l’ iesimo dato della sequenza:
MOV AL, DatiOut[SI]
OUT BufferOut, AL
INC SI
; trasferimento in AL del contenuto della cella
; di indice SI del vettore DatiOut. Se il programma
; sta inviando l’i-esimo dato SI, che è stato
; opportunamente inizializzato, contiene il valore i-1
; trasferimento del contenuto di AL nel registro
; mappato all’indirizzo “BufferOut” dello spazio di I/O.
; incremento dell’indice del vettore, che così punta
; alla cella di memoria da cui deve essere prelevato
; il prossimo dato da inviare all’interfaccia
14
Le modalità di gestione dell’I/O
Affrontiamo ora in modo più approfondito la problematica della sincronizzazione fra
CPU (programma in esecuzione sulla CPU) ed Unità di I/O. Come già osservato, la
funzionalità di sincronizzazione è a carico dell’interfaccia di I/O.
Operazione di Input
CPU
B
u
f
f
e
r
I
n
Unità
I/O
Interfaccia
L’interfaccia deve comunicare alla
CPU che l’Unità di I/O ha reso
disponibile un nuovo dato su BufferIn
(condizione di “BufferIn Full”)
Operazione di Output
CPU
B
u
f
f
e
r
O
u
t
Unità
I/O
Interfaccia
L’interfaccia deve comunicare alla CPU
che l’Unità di I/O ha prelevato da
BufferOut
il dato precedentemente
inviato dalla CPU e quindi la CPU può
inviare un nuovo dato (condizione di
“BufferOut Empty”).
Le interfacce possono comunicare alla CPU le informazioni di sincronizzazione in due
modi diversi, cui corrispondono due differenti modalità di gestione dell’I/O
dette
15
Gestione a Polling (Controllo di Programma) e Gestione ad Interrupt (Interruzione).
Gestione a Polling
Memoria
BOE: BufferOut Empty
BIF: BufferIn Full
DatiOut
Status
BOE BIF
Interfaccia
BufferIn
BufferOut
CPU
DatiIn
L’interfaccia rende disponibili le informazioni di sincronizzazione su 2 bit
del registro di stato. La CPU, ogni qual volta deve eseguire un trasferimento,
vaprima a leggere il registro di stato (BIF oppure BOE) per verificare se
l’interfaccia è pronta per il trasferimento. Se l’interfaccia è pronta la CPU
esegue il trasferimento, viceversa legge in continuazione il registro di stato
fino a che questo non segnala che l’interfaccia è pronta, quindi esegue il
16
trasferimento.
Flusso di un
programma
che esegue una
Operazione di Input
a Polling.
(1)
Inizializzazione
Operazione di I/O
(2)
Lettura Registro
Status
(3)
BIF=1
Codifica in Assembler
(1)
(2) WaitDato:
MOV SI, 0
IN AL, Status
(3)
TEST AL, 1
JZ WaitDato
IN AL, BufferIn
MOV DatiIn[SI], AL
INC SI
CMP SI, N
JNE WaitDato
(4)
(5)
(6)
No
Si
(4)
Lettura Registro
BufferIn
(5)
Scrittura Vettore
DatiIn e
incremento indice
(6)
Fine
Operazione
di I/O
Si
No
17
Considerazioni sulla Gestione a Polling
•  Nella Gestione a Polling la CPU è costantemente impegnata ad osservare lo stato
della periferica al fine di eseguire un trasferimento non appena quest’ultima
risulta pronta.
•  Conseguentemente, la CPU non può svolgere nessuna altra attività durante gli
intervalli di tempo che intercorrono fra due situazioni successive di “interfaccia
pronta” (tempo necessario al trasferimento del dato fra interfaccia e Unità di I/O).
Poiché le Unità di I/O sono tipicamente molto più lente della CPU, ciò implica una
scarsa efficenza nell’impiego delle CPU.
•  La Gestione ad Interrupt, che vedremo successivamente, ha proprio l’obiettivo di
impiegare la CPU in modo più efficiente, consentendogli di eseguire delle istruzioni
“utili” negli intervalli di tempo che intercorrono fra due situazioni successive di
“interfaccia pronta”.
•  Tuttavia, la Gestione a Polling è molto più semplice di quella ad Interrupt. Inoltre,
quest’ultima presenta dei vantaggi reali solo quando è possibile strutturare il
software in maniera tale che esistano delle attività utili che possono essere svolte
simultaneamente all’Operazione di I/O.
18
Gestione ad Interrupt
Interfaccia
Priority/Enable
Memoria
INTR
DatiOut
INT
BOE BIF
BufferIn
BufferOut
CPU
DatiIn
Quando l’interfaccia è pronta ad eseguire un nuovo trasferimento invia alla CPU una richiesta di
servizio (detta Interrupt) mediante un apposito segnale (INTR, che per ora possiamo considerare
connesso all’ingresso INT della CPU). All’atto della ricezione della richiesta di servizio la CPU
interrompe il programma in esecuzione e trasferisce il controllo (secondo un meccanismo che
studieremo successivamente) ad una apposita procedura di servizio (detta Interrupt Handler) che si
occupa di effettuare il trasferimento del dato. Una volta effettuato il trasferimento la procedura di
servizio restituisce il controllo al programma che era in esecuzione all’atto della ricezione
dell’Interrupt.
Nella Gestione ad Interrupt la CPU può eseguire qualsiasi attività durante gli intervalli di tempo che
intercorrono fra due situazioni successive di “interfaccia pronta” (cioè fra due interrupt).
19
Flusso di un programma che esegue una
Operazione di Input ad Interrupt
Interrupt Handler
Main Program
(1)
INT
(8)
Inizializzazione
Operazione di I/O
Attività “Utile”
Fine
Operazione
di I/O
Si
(2)
Prologo della
Procedura
(3)
Lettura Registro
BufferIn
(4)
Scrittura Vettore
DatiIn e
incremento indice
(5)
Ultimo
Dato
No
No
(7)
Epilogo della
Procedura
Si
Segnalazione
(6)
Fine Op. di I/O
20
Codifica in Assembler
Interrupt Handler
Main Program
(1)
MOV Index, 0
MOV FineOp,0
Attività “Utile”
(2)
(3)
(4)
INT
(8) WaitFineOp: CMP FineOp, 1
JNE WaitFineOp
(5)
(6)
(7)Epilogue:
PUSH AX
PUSH SI
MOV SI, Index
IN AL, BufferIn
MOV DatiIn[SI], AL
INC SI
MOV Index, SI
CMP SI, N
JNE Epilogue
MOV FineOp, 1
POP SI
POP AX
IRET
21
Considerazioni sulla Gestione a Interrupt
•  La Gestione ad Interrupt consente un utilizzo efficiente della CPU: la CPU può
essere impegnata in altre attività durante i “tempi morti” che intercorrono fra gli
istanti in cui l’interfaccia è pronta ad eseguire un nuovo trasferimento.
•  E’ opportuno sottolineare che se l’Operazione di I/O richiede il trasferimento di N
gruppi di dati l’interfaccia genererà N richieste di servizio (cioè N Interrupt) e
conseguentemente l’Interrupt Handler verrà eseguito N volte. Ad ogni esecuzione
l’Interrupt Handler trasferisce un solo gruppo (che può corrispondere, ovviamente,
a un solo dato)
•  Poiché i trasferimenti sono eseguiti dall’Interrupt Handler, è necessario prevedere
un meccanismo di sincronizzazione fra quest’ultimo ed il Main Program.
Nell’esempio mostrato la sincronizzazione è effettuata tramite la variabile FineOp,
azzerata dal Main Program nella fase di inizializzazione e messa a 1 dall’Interrupt
Handler quando ha completato l’Operazione di I/O.
•  La Gestione ad Interrupt è particolarmente adatta ai Sistemi Multitasking: un task
(programma), dopo aver “lanciato” l’Operazione di I/O viene “sospeso” dal
Sistema Operativo e la CPU viene assegnata ad un altro task. Il task sospeso viene
poi rimesso in esecuzione quando l’Operazione di I/O è stata completata
22
dall’Interrupt Handler (che fa parte del kernel del Sistema Operativo).