Personal computer/Mapping
Nei capitoli precedenti abbiamo definito le caratteristiche logiche di CPU, sistema di memoria ed interfacce di Input/Output, in questo capitolo vedremo come collegare chip di memoria ed interfacce ad una CPU con parallelismo 8 bit.
Il progetto di elaboratore elettronico è basato sulla CPU 8088, se ne osserverà il pin-out e ci si concentrerà sulle caratteristiche logiche e sulle temporizzazioni dei segnali in ingresso alla CPU, in particolare il segnale di ready comandato dall'integrato 8284.
Per il sottosistema di memoria si vedrà il funzionamento di EPROM e RAM.
Per le interfacce se ne osserverà una generica, con considerazioni del tutto indipendenti dal reale dispositivo periferico controllato, per poi passare allo studio di interfacce di comunicazione standard quali una porta seriale COM (Interfaccia 8250 gestita tramite il protocollo RS 232) ed una porta parallela (8259).
Per la maggiore semplicità si considerano le interfacce gestite da programma (gestione a polling), tuttavia verrà preso in considerazione un integrato (PIC 8259) per la gestione delle interfacce tramite interruzioni (gestione a interrupt).
Caratteristiche e funzionamento degli integrati notevoli
[modifica | modifica sorgente]Decodifica degli indirizzi
[modifica | modifica sorgente]Qualsiasi dispositivo fisico, che sia una memoria o una interfaccia, per essere accessibile dal software deve poter essere indirizzato, ovvero deve occupare una finestra dello spazio di indirizzamento.
Il mapping consiste nell'associare ad un dispositivo una porzione dello spazio di indirizzamento e di generare, a partire dai bit sul bus degli indirizzi e dai segnali di comando, un segnale di selezione per quel dispositivo.
Sia un'interfaccia che una memoria, ma vale lo stesso discorso per qualsiasi agente master indirizzabile (controller DMA, controller hard disk, ...), occupa n=2^k posizioni nello spazio di indirizzamento. n rappresenta il numero di oggetti (ad esempio un registro) di 8 bit indirizzabili, k il numero di bit di indirizzo per il dispositivo.
Avendo a disposizione k indirizzi, che si traducono in k connessioni, qualsiasi dispositivo ha al suo interno un decoder di k variabili con un ingresso di enable detto chip select o chip enable che consente alla CPU di abilitare soltanto il dispositivo selezionato.
Quasi sempre il mapping dei dispositivi è allineato, ovvero l'indirizzo più basso tra tutti quelli associati al dispositivo è un multiplo della dimensione in byte dello stesso. L'allineamento semplifica notevolmente la decodifica degli indirizzi. Supponiamo infatti di avere un dispositivo con A indirizzi mappato in uno spazio di indirizzamento da 2^a bit (a bit di indirizzo), posto i = A - a esistono 2^i posizioni in cui poter mappare il dispositivo, tuttavia soltanto se questo è allineato gli a indirizzi dello spazio di indirizzamento possono essere considerati come il concatenamento di A e i. Supponendo di avere uno spazio di indirizzamento di 10 bit e voler mappare un dispositivo da 256 byte = 2^8. Allineando il dispositivo i due bit più significativi dello spazio di indirizzamento indicano che sono possibili 4 finestre (2^2) che sono appunto i byte da 0-255, 256-511, 512-767, 768-1024. Osservando la configurazione binaria degli indirizzi le quattro finestre sono (in parentesi l'indirizzo esadecimale)
00 0000 0000 ( 0 00H ) 00 1111 1111 ( 0 FFH ) 01 0000 0000 ( 1 00H ) 01 1111 1111 ( 1 FFH ) 10 0000 0000 ( 2 00H ) 10 1111 1111 ( 2 FFH ) 11 0000 0000 ( 3 00H ) 11 1111 1111 ( 3 FFH )
Gli 8 bit meno significativi saranno decodificati internamente al dispositivo per selezionare il relativo elemento di memoria, i due bit più significativi serviranno invece per selezionare il dispositivo in una delle quattro finestre.
Sono possibili due tipi di decodifica degli indirizzi:
- decodifica completa
- decodifica semplificata
Decodifica completa
[modifica | modifica sorgente]Le decodifica completa è effettuata utilizzando tutti i bit di offset dell'indirizzo, i bit più significativi che non vengono utilizzati dal dispositivo per decodificare internamente una cella di memoria. Nell'esempio precedente di bus indirizzi a 10 bit e elemento di memoria a 8 bit, la decodifica completa è effettuata decodificando i 2 bit più significativi dell'indirizzo.
La decodifica più semplice è calcolare per ogni dispositivo il mintermine corrispondente allo spazio occupato, posto, ad esempio, che l'elemento di memoria sia nella parte più bassa dello spazio di indirizzamento ( 0-255 byte) allora i due bit più significativi dell'indirizzo valgono 0. Il chip select del dispositivo sarà, in logica positiva, CS = A9 * A8 realizzabile con un AND.
È possibile anche una decodifica con decoder 2:4 che presenta in uscita tutte le possibili finestre allineate. L'uscita del decoder corrispondente alla porzione di spazio di indirizzamento in cui è mappato il dispositivo è collegata con il suo ingresso chip select.
L'elemento più usato per la decodifica degli indirizzi è la PAL, una rete logica ad i ingressi e o uscite, identificata da iLo (ad esempio 10L8), che permette di generare o chip select a partire da i bit di ingresso. La PAL è una rete programmabile tramite espressioni in logica positiva ma le uscite sono già negate, per cui direttamente collegabili al CS* dei dispositivi. La scelta di una PAL si dimostra conveniente soprattutto quando i bit di selezione sono molti, per realizzare la stessa funzione di una PAL 10L8 sarebbe necessario un decoder a 10 ingressi e quindi a 1024 uscite.
Decodifica semplificata
[modifica | modifica sorgente]La decodifica semplificata o parziale viene effettuata eliminando alcuni bit dall'espressione dei chip select dei dispositivi mappati, mantenendo il corretto funzionamento del sistema.
Se considero un sistema con indirizzi a 10 bit e due elementi di memoria da 256 celle ciascuno ( 8 bit ) mappate in questo modo
0 00H 0 FFH -> chip1 1 00H 1 FFH -> chip2 2 00H 2 FFH 3 00H 3 FFH
I segnali dei chip select in logica positiva sono
CSchip1 = !A9 * !A8 CSchip2 = !A9 * A8
Le due espressioni dei chip select individuano univocamente una finestra di 256 byte all'interno dello spazio di indirizzamento, che tuttavia presenta degli spazi liberi (la metà alta). Si può quindi pensare di eliminare l'univocità delle finestre selezionate mantenendo la separazione delle finestre.
Ad esempio eliminando il bit A9 dell'espressione del chip select si ottiene
CSchip1 = !A8 CSchip2 = A8
che indirizzano le seguenti finestre
0 00H 0 FFH <- CSchip1 1 00H 1 FFH <- CSchip2 2 00H 2 FFH <- CSchip1 3 00H 3 FFH <- CSchip2
Ogni segnale di chip select individua due finestre nello spazio di indirizzamento, ognuna da 256 byte. Con queste espressioni dei chip select il chip1 risulta mappato in due finestre, così come il chip2 ma non essendoci sovrapposizione tra i chip select dei due dispositivi questa codifica semplificata risulta corretta.
Mentre da un lato con la decodifica parziale si ottengono delle espressioni semplificate per i chip select (nell'esempio un solo bit piuttosto che due), dall'altro si ha un forte spreco delle risorse. Eliminando n bit dall'espressione dei chip select ogni dispositivo va ad occupare 2^n posizioni.
Gestione delle interfacce tramite programma
[modifica | modifica sorgente]Una generica interfaccia ha dei registri interni che possono essere buffer dati (ingresso/uscita), registri di stato, di controllo, e che devono essere mappati nello spazio di I/O ed accessibili dalla CPU.
Ipotizziamo le seguenti associazioni
Buffer Ingresso registro all'indirizzo 80H Buffer Uscita registro all'indirizzo 81H Status registro all'indirizzo 82H Control registro all'indirizzo 83H Control Word dato binario che è necessario scrivere nel registro di controllo per assicurare il corretto funzionamento dell'interfaccia, valore D4H
Il programma per prima cosa dovrà inizializzare l'interfaccia
BufferIn EQU 80H ; l'istruzione EQU definisce degli identificatori BufferOut EQU 81H ; associati a delle costanti StatusRegister EQU 82H ControlRegister EQU 83H ControlWord EQU D4H MOV AL, ControlWord ; carica in AL la costante ControlWord OUT ControlRegister, AL ; e trasferiscila nel registro associato a ControlRegister
Una volta inizializzata l'interfaccia sono possibili le operazioni di
Lettura dello stato
IN AL, StatusRegister ; carica in AL il contenuto del registro di stato dell'interfaccia
Lettura di un dato, Input
IN AL, BufferIn ; Preleva dal Buffer il contenuto del registro MOV Dati[SI], AL ; memorizzane il valore nel vettore Dati all'indirizzo SI INC SI ; incrementa l'indice del vettore per poter prelevare un altro dato dal buffer
Scrittura di un dato, Output
MOV AL, Dati[SI] ; Copia il valore della cella del vettore da trasferire OUT BufferOut, AL ; memorizzala nel registro Buffer Out INC SI ; incrementa l'indice del vettore per poter trasferire un altro dato del vettore
Le operazioni di lettura e scrittura necessitano una sincronizzazione, l'interfaccia deve comunicare alla CPU che è disponibile un dato nel Buffer Input o che il Buffer Output è vuoto e quindi scrivibile.
La sincronizzazione, come visto in generale per le interfacce di I/O può avvenire per
- polling o a controllo del programma
- interrupt o con interruzioni
Gestione a Polling
[modifica | modifica sorgente]L'interfaccia rende disponibili le informazioni di sincronizzazione nel registro di stato tramite due bit
- BIF Buffer Input Full' ad indicare che il buffer in input è pieno, quindi contiene un valore che può essere letto dalla CPU
- BOE Buffer Output Empty il buffer di uscita è vuoto e quindi scrivibile dalla CPU
Prima di eseguire il trasferimento, la CPU deve controllare lo stato dell'interfaccia, un esempio del programma potrebbe essere
Input
MOV SI, 0 ; Inizializzazione dell'operazione di trasferimento WaitDato: IN AL, Statuts ; Lettura del registro di stato TEST AL, 1 ; valuta il registro di stato (BIF) JZ WaitDato ; cicla le due operazioni precedenti se BIF = 0 IN AL, BufferIn ; altrimenti leggi il registro di buffer in input MOV Dati[SI], AL ; memorizzane il valore in un array INC SI ; incrementa il contatore per ulteriori letture CMP SI, N ; verifica se l'operazione di lettura è terminata JNE WaitDato ; se non è terminata torna ad attendere un dato
Output
MOV SI, 0 ; Inizializzazione dell'operazione di trasferimento WaitDato: IN AL, Statuts ; Lettura del registro di stato TEST AL, 1 ; valuta il registro di stato (BOE) JZ WaitDato ; cicla le due operazioni precedenti se BOE = 0 MOV AL, Dati[SI] ; altrimenti prepara il dato da inviare all'interfaccia OUT BufferOut, AL ; e copialo INC SI ; incrementa il contatore per ulteriori scritture CMP SI, N ; verifica se l'operazione di scrittura è terminata JNE WaitDato ; se non è terminata torna ad attendere la liberazione del buffer
Esempio di programmi per la gestione delle interfacce
[modifica | modifica sorgente]Pensiamo di voler gestire dei led mappati all'indirizzo 60H, per poter essere collegati al bus dati è necessario interfacciarli con un 373 che mette in 3-state le connessioni tra led e bus quando i primi non sono indirizzati, il programma, dopo l'inizializzazione
Led EQU 60H ; etichetta per l'indirizzo dei led
comanda l'accensione di tutti i led
MOV AL, FFH ; costruisce il byte ... OUT Led, AL ; ... da inviare all'interfaccia
Gestione ad Interrupt
[modifica | modifica sorgente]Nella gestione ad interrupt la CPU esegue un programma principale qualsiasi e viene interrotta da una richiesta di passaggio alla routine di servizio associata all'interruzione. La procedura chiamata dall'interfaccia deve quindi avere un prologo e un epilogo che permettano alla CPU di poter tornare ad eseguire il programma principale alla fine della routine di servizio
Programma principale
MOV Index, 0 MOV FineOp, 0 . . ; in questa fase la CPU esegue altre operazioni e può accettare gli interrupt . WaitFineOp: CMP FineOp, 1 JNE WaitFineOp
La variabile FineOp è usata per la sincronizzazione, viene azzerata in fase di inizializzazione dal programma principale, ed impostata ad 1 dal gestore delle interruzioni (la routine di servizio) quando è completata l'operazione di I/O.
Routine di servizio
PUSH AX ; Memorizza i registri PUSH SI ; MOV SI,Index ; e inizializza il trasferimento IN AL,BufferIn ; leggi dal buffer MOV DatiIn[SI],AL ; salva i dati letti INC SI ; incrementa il puntatore MOV Index,SI ; e memorizzalo in una variabile accessibile alle successive routine CMP SI,N ; verifica se il trasferimento è finito JNE Epilogo MOV FineOp,1 ; se lo è indicalo anche al programma principale Epilogo: POP SI ; ripristina i registri POP AX IRET ; ritorna al programma principale