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).