Che materia stai cercando?

Programmable Interrupt Controller

Tesina di Calcolatori elettronici II del prof. Mazzeo su Implementazione VHDL di un Programmable Interrupt Controller (PIC) mod. Intel 8259A: PIC 8259A/simple, Interfaccia del dispositivo 8259A e indirizzamento, Progettazione VHDL di basso livello, Implementazione dei componenti.

Esame di Calcolatori Elettronici II docente Prof. A. Mazzeo

Anteprima

ESTRATTO DOCUMENTO

Facoltà di Ingegneria

Corso di Studi in Ingegneria Informatica

Esame di Calcolatori Elettronici II

tesina di fine corso

Implementazione VHDL di un Programmable Interrupt Controller

(PIC) mod. Intel 8259A

Anno Accademico 2007-2008

Docente

Ch.mo prof. Antonino Mazzeo

Studente

Anzivino Antonio – 534/1922

PIC 8259A/simple

L’obiettivo di questo progetto è implementare in linguaggio VHDL un controller interrupt programmabile (PIC)

secondo le specifiche del modello Intel 8259A, uno dei più diffusi chip per la realizzazione di funzionalità di

interruzione.

Lo scopo di un controller interrupt programmabile è la gestione, per conto della CPU, delle richieste di interruzione

provenienti dai dispositivi periferici connessi al calcolatore. L’Intel 8259A è stato a lungo adottato nelle architetture

basate su x86 (Intel e AMD), ma, con l’evolvere dei calcolatori, il dispositivo PIC è stato sostituito da un più avanzato

APIC (Advanced PIC), che non sarà oggetto di trattazione.

La caratteristica di rilievo di un controller PIC è proprio la programmabilità: come si può vedere sul datasheet, l’Intel

8259A possiede diverse modalità di funzionamento, ed è possibile programmare da CPU il modo in cui le interruzioni

dovranno essere gestite. Il PIC, infatti, è in grado di mascherare le interruzioni che non dovranno essere servite in un

dato momento, in quanto il processore sta eseguendo compiti più importanti. La programmazione del PIC è

un’operazione che viene eseguita all’accensione del

calcolatore, mediante BIOS, ma le impostazioni possono

essere sovrascritte dal sistema operativo in fase di

caricamento, in quanto il kernel gira in modalità

Supervisore con libero accesso all’hardware.

Nei calcolatori moderni, il controller PIC è integrato sulla

scheda madre, all’interno del Southbridge, che include

tutti i dispositivi di interfacciamento con i bus di I/O, come

PCI, PCI-Express, IDE, porte seriali e parallele.

Il Southbridge gestisce le operazioni più lente del

calcolatore, come presto vedremo.

Assieme alla piedinatura, il datasheet fornisce dettagliate

indicazioni sui registri che possono essere programmati nel

PIC, e una completa descrizione del protocollo di

interruzione. Tuttavia, per motivi di praticità e di tempo,

non siamo stati in grado di implementare tutte le

funzionalità, e pertanto ci limiteremo ad implementare un

PIC a priorità che implementa unicamente il protocollo di

segnalazione a livelli di priorità.

1. Interruzioni e operazioni di I/O

Iniziamo con una panoramica sul problema delle

interruzioni. Le operazioni di I/O sono l’elemento chiave di

un calcolatore, in quanto esso non è un sistema autistico

ma deve comunicare con l’esterno: tanto con un operatore umano quanto con un altro calcolatore.

Esempi tipici di I/O sono i seguenti:

 Visualizzazione a schermo di testo su una console di comando

 Visualizzazione a schermo di una interfaccia grafica bidimensionale (GUI)

 Rendering tridimensionale (applicazioni CAD e videogiochi)

 Lettura e scrittura di dati su memoria di massa

 Trasmissione di dati ad un altro calcolatore attraverso una rete di calcolatori, come Internet

 Scansione di un documento di testo

 Stampa di fotografie

 Riproduzione e registrazione di segnali audiovisivi

 Interazione utente con tastiera e mouse/trackball o touchscreen (lettura dei comandi impartiti dall’utente

sull’interfaccia attraverso i suddetti dispositivi)

Tali operazioni, così come tutte le altre che non riteniamo di elencare, vengono eseguiti da dispositivi che, anche

se interni al calcolatore, sono esterni alla CPU che esegue il software applicativo. In un tipico scenario,

supponendo che l’utente voglia spedire un messaggio di posta elettronica, i dispositivi di I/O coinvolti sono tanti

e ognuno ha una specifica funzione. Descriviamo quindi lo scenario come segue:

1. L’utente utilizza il mouse per posizionare il puntatore sul comando “Crea messaggio” (input)

2. Mentre l’utente esegue l’azione al punto 1, il puntatore sullo schermo segue i movimenti dell’utente

(output)

3. L’utente fa click sul comando (input)

4. A schermo viene visualizzata una finestra di composizione messaggio (output)

5. L’utente richiede che venga aperta la rubrica per selezionare il destinatario (input)

6. Il sistema legge la rubrica da disco (input)

7. Il sistema visualizza i contatti a schermo sotto forma di una lista (output)

8. L’utente seleziona il destinatario (input)

9. L’utente scrive il testo del messaggio sulla tastiera (input)

10. A schermo vengono visualizzati i caratteri digitati dall’utente, che così può correggere eventuali errori

(output)

11. L’utente fa click sul comando “Invia messaggio” (input)

12. Il sistema trasmette un pacchetto di sincronizzazione TCP verso il server di posta in uscita (output)

13. Il sistema attente un acknowledgement TCP dal server (input)

14. Il messaggio viene trasmesso secondo il protocollo SMTP (output, ma vi sono anche input in realtà)

15. A schermo viene visualizzata una conferma dell’avvenuto invio

In questo semplice scenario, i dispositivi coinvolti nell’interazione sono tastiera, mouse, schermo, disco rigido e

scheda di rete. Ognuno ha il proprio compito, e il processore, guidato dal sistema operativo e dal software

applicativo, deve far sì che il loro utilizzo corretto porti allo scopo preposto, nel caso l’invio di un messaggio email.

Il problema è che le operazioni di I/O sono estremamente lente rispetto alla velocità di esecuzione delle istruzioni di

una CPU. Un dattilografo difficilmente supera la velocità di 5 tasti premuti al secondo, mentre una CPU in grado di

completare un’istruzione al secondo (ciò si può ottenere col pipelining, ad esempio), alla frequenza di soli 100MHz è

in grado di completare 100 milioni di istruzioni al secondo.

È ovvio che, nel caso di un calcolatore che stia eseguendo molti compiti, come la navigazione sul web, la

compilazione di un programma e la masterizzazione di un DVD, è improponibile che il processore si metta in attesa

attiva dell’input utente da tastiera, ossia non esegua alcun compito fin quando l’utente non avrà premuto un tasto.

La prima soluzione, apparentemente, a questo problema, sarebbe quella per la CPU di interrogare (polling) uno a

uno i dispositivi di input per stabilire quale desideri essere servito: nel nostro caso, non sappiamo se in un dato

momento è la tastiera a richiedere che venga letto un carattere o il mouse che venga rilevato il suo movimento. La

soluzione sincrona, però, non si presta all’applicazione reale, in quanto la CPU spreca inutilmente cicli nella fase di

polling.

La soluzione al problema è asincrona: si fa in modo che siano le periferiche stesse a interrompere la CPU quando è

richiesta attenzione. Per la verità, una periferica non può letteralmente interrompere la CPU, ma ciò è solo l’effetto

macroscopico che si vede eseguendo i programmi a velocità CPU. Per implementare le interruzioni, è innanzi tutto

necessario un passo teorico: una piccola modifica al ciclo di Von Neumann, che diviene il seguente:

1. Fetch

2. Operand Assembly

3. Execute

4. IF NOT Interrupt THEN

 Serve Interrupt

5. GoTo 1

La CPU, dunque, al termine di ogni istruzione, verifica se vi è una richiesta di interruzione da parte di una periferica, e

in tal caso si prepara a servire tale interruzione.

Fatto ciò, si è subito visto che vi sono servizi da considerarsi time critical, ossia che è importante che vengano serviti

prima di altri tipi di servizi di minore importanza. Ad esempio, in un impianto industriale, l’interruzione proveniente

da un controller termostatico che segnala l’eccessiva temperatura di un altoforno dovrebbe far immediatamente

scattare una routine di emergenza che spenga l’impianto. Ciò dovrebbe avere massima priorità, senz’altro più alta di

quella di un dispositivo che segnala l’avvenuta produzione di un pezzo.

Dunque, nei processori moderni, gli interrupt sono organizzati a priorità. La priorità di un dispositivo è detta IRQ. Per

convenzione, sui dispositivi 8259, ha priorità maggiore il dispositivo con IRQ0.

La convenzione va oltre la specifica delle priorità: la maggior parte dei dispositivi assume un IRQ specifico. Wikipedia

ci riporta una tabella nella quale possiamo osservare quali siano, nell’architettura x86, gli IRQ tipicamente assegnati

a determinate periferiche:

IRQ 0 - System timer. Reserved for the system. Cannot be changed by a user.

IRQ 1 - Keyboard. Reserved for the system. Cannot be altered even if no keyboard is

present or needed.

IRQ 2 - Cascaded signals from IRQs 8-15. A device configured to use IRQ 2 will actually

be using IRQ 9

IRQ 3 - COM2 (Default) and COM4 (User) serial ports

IRQ 4 - COM1 (Default) and COM3 (User) serial ports

IRQ 5 - LPT2 Parallel Port 2 or sound card

IRQ 6 - Floppy disk controller

IRQ 7 - LPT1 Parallel Port 1 or sound card (8-bit Sound Blaster and compatibles)

IRQ 8 - Real time clock

IRQ 9 - Free / Open interrupt / Available / SCSI. Any devices configured to use IRQ 2

will actually be using IRQ 9.

IRQ 10 - Free / Open interrupt / Available / SCSI

IRQ 11 - Free / Open interrupt / Available / SCSI

IRQ 12 - PS/2 connector Mouse. If no PS/2 connector mouse is used, this can be used for

other peripherals

IRQ 13 - ISA / Math Co-Processor

IRQ 14 - Primary IDE. If no Primary IDE this can be changed

IRQ 15 - Secondary IDE

Come possiamo notare, il timer di sistema ha priorità massima, il controller IDE primario ha priorità sul secondario

ma non sul coprocessore matematico né sulle porte seriali COM.

Possiamo verificare ciò empiricamente: nel nostro caso abbiamo constatato, attraverso i tool forniti dal sistema

operativo, l’assegnazione degli IRQ su un processore AMD64 per quanto riguarda il timer di sistema.

Ciò significa che, nel caso in cui sia la porta

parallela LPT1 che la tastiera sollevino un

interrupt, verrà servita prima la tastiera e poi la

porta parallela.

In un tipico protocollo di I/O asincrono, CPU e

periferica comunicano tra di loro per concordare

l’operazione e in seguito scambiare i dati.

Ricordiamo che l’accesso all’I/O è riservato al

sistema operativo, quindi un software (come il

client di posta che vuole caricare la rubrica)

dovrà chiedere al kernel di eseguire l’azione per

suo tramite.

Sebbene la gestione del file system sia

incredibilmente complicata, per ragioni di

robustezza e sicurezza, possiamo semplificare

tutto ciò riducendo il caricamento della rubrica

da disco ad una serie di passi più semplici:

1. Il client prepara i registri del

processore in una configurazione conforme alle

specifiche del sistema operativo. Ad esempio, il

1

registro A0 viene caricato col puntatore alla stringa che contiene nome e percorso del file da caricare,

D0 viene caricato con i flag di sola lettura e A1 con il puntatore al buffer dove dovrà essere caricato il

contenuto del file.

2. Il client effettua dunque una system call, facendo in modo che il processore entri in stato Supervisore ed

esegua le routine di file system del kernel. Da adesso in poi, il controllo è del sistema operativo.

3. Il sistema verifica la validità dei dati e individua il disco dove essi sono presenti, nonché le esatte

coordinate da caricare

4. Il sistema scrive su un apposito registro del controller del disco il settore da caricare e abilita il flag di

lettura

5. A questo punto, il sistema operativo, sapendo di dover attendere diverse decine di millisecondi prima

del caricamento, sospende il thread in esecuzione e passa il controllo ad uno in attesa di essere eseguito

6. Il disco, una volta letti i dati e caricati nel suo buffer interno, segnala alla CPU il completamento

dell’operazione sollevando un interrupt

7. La CPU sospende l’esecuzione e individua che la richiesta proviene dal disco rigido quindi conferma

l’avvenuto riconoscimento dell’interrupt (INT acknowledgement)

8. La CPU esegue una routine del kernel che completa il caricamento dei dati spostandoli dal buffer disco

alla memoria, nella locazione desiderata dal thread richiedente. Il protocollo con il disco termina

9. Il sistema operativo, in base alle proprie regole di schedulingl, decide se restituire il controllo al client di

posta

Finora non abbiamo trattato in adeguato dettaglio la parte hardware della gestione delle interruzioni, soffermandoci

sul software.

Quando si produce un calcolatore, in particolare la sua scheda madre, tutti i dispositivi in essa integrati vengono

fisicamente connessi, in maniera permanente, alle varie linee di controllo e dati che mettono in comunicazione i

componenti del sistema. Un metodo semplice ed economico per gestire le interruzioni a priorità è la daisy chain,

implementata nei sistemi basati su 68000.

1 Faremo sempre riferimento al Motorola 68000

La daisy chain è come una lista concatenata: i dispositivi sono connessi in parallelo al bus dati e la linea INT, ma lo

sono in serie per quanto riguarda la linea INTA, così che ogni dispositivo è connesso al successivo (per INTA).

Per convenzione, diciamo che il dispositivo 0 è quello connesso direttamente alla CPU mentre il dispositivo N-1 è

l’ultimo della catena. Quando un dispositivo I intende sollevare un interrupt, alza la linea INT sulla linea comune,

tipicamente realizzata con tecnologia open drain/open collector. Anziché effettuare il polling, la CPU alza INTA su 0,

e la regola è la seguente: quando un dispositivo J riceve INTA dal precedente, se non ha richiesto interruzioni allora

propaga INTA su J+1, altrimenti prosegue con il protocollo. In questo modo, anche il segnale INTA si propagherà fino

al destinatario.

Quando il dispositivo che ha richiesto INT riceve INTA, capisce di essere in fase di servizio e scrive sul bus dati il suo

identificativo, in modo che la CPU capisca quale dispositivo ha richiesto l’interrupt. Quindi la CPU, letto l’ID, abbassa

INTA e il dispositivo abbassa INT.

In questa daisy chain, come possiamo osservare dall’algoritmo di propagazione di INTA, il dispositivo fisicamente più

vicino alla CPU ha la massima priorità, mentre quello a fine catena la minore. Infatti possiamo affermare che per

servire la INT di un dispositivo K non ci deve essere nessun dispositivo K-1 a richiedere interruzioni

contemporaneamente.

L’uso del PIC, a fronte di ciò, è legato alla possibilità di controllare dinamicamente il comportamento degli interrupt,

senza dover implementare su ogni anello della daisy chain un apposito algoritmo. In questo caso, tutte le linee INT

dei dispositivi sono connesse al PIC in parallelo, e il PIC esce con un’unica linea INT verso la CPU, dalla quale riceve

INTA. La programmazione del PIC fa sì che il criterio di selezione del dispositivo da servire possa cambiare

dinamicamente. Ad esempio, l’ultimo dispositivo servito potrebbe diventare quello dalla minor priorità, oppure si

può usare un algoritmo round robin per le priorità. In questi casi, la convenzione fatta sulla numerazione degli IRQ è

superflua, in quanto, in un dato momento, IRQ(i) potrebbe avere meno priorità di IRQ(i+1).

2. Interfaccia del dispositivo 8259A e indirizzamento

Esaminiamo i connettori dell’8259A come indicati sul datasheet Intel:

Nome pin Input/Output Descrizione

Vcc I Alimentazione 5V

GND I Alimentazione massa

CS’ I Chip select: quando basso, abilita le comunicazioni con la CPU. Viene

collegato ad un comparatore connesso al bus indirizzi

WR’ I Write: quando basso, abilita il PIC a ricevere comandi

RD’ I Read: quando basso, abilita il PIC a inviare dati alla CPU sul bus dati

D7-D0 I/O Bus dati: linee bidirezionali per la comunicazione con la CPU

CAS0-CAS2 I/O Cascade: controllano i PIC in cascata. Sono di output per il master e di

input per lo slave

SP’/EN’ I/O Slave program/Enable buffer: doppia funzionalità. Quando non è in

modalità buffer, designa uno slave, altrimenti comanda i trasmettitori

del buffer

INT O Interrupt: interrompe la CPU

IR0-IR7 I IRQ: le periferiche vengono connesse ad uno dei pin rispettando la

regola del lower-priority

INTA I Interrupt acknowledgement: la CPU conferma che è pronta a servire la

richiesta

A0 I A0: parte del meccanismo di indirizzamento dei registri PIC

In totale ci sono 28 pin. Osserviamo che il PIC è una rete asincrona, in quanto non vi sono segnali di clock in ingresso.

Analizziamo subito la particolare modalità di indirizzamento del PIC: un controller PIC ha due indirizzi, uno pari ed

uno dispari. I bit più significativi dell’indirizzo vengono confrontati con i bit più significativi del bus indirizzi per

determinare se il PIC è stato selezionato, mentre il bit meno significativo (A0) viene inviato direttamente al PIC. Ciò ci

permette di indirizzare due registri mediante il bus indirizzi. In realtà, il PIC contiene al suo interno un particolare

meccanismo di indirizzamento che usa il bit A0 in concomitanza con alcuni bit della parola dati (D7-D0) per

indirizzare più di due registri. Questo perché il PIC ha 4 registri ICW (Instruction Command Word) e 3 OCW

(Operation Command Word) da 8 bit ognuno.

Ad esempio, ICW2 viene indirizzato in scrittura dopo aver scritto su ICW1. La sincronizzazione delle operazioni

avviene attraverso pulsazioni del segnale INTA in logica 0. Programmare il PIC significa, come già illustrato, definirne

la modalità di funzionamento: è possibile impostare la modalità di selezione delle priorità, il tipo di protocollo

(perché si può usare uno stesso 8259A su diverse architetture)o la maschera.

Nella nostra implementazione, apporteremo delle semplificazioni al protocollo di interrupt, e non andremo a

prevedere alcuna programmazione.

In particolare, dal punto di vista comportamentale, ci aspettiamo quanto segue:

1. Quando il PIC riceve un interrupt su una o più linee IRx, determina il dispositivo con maggiore priorità

attraverso la regola semplice dell’ID minore, quindi alza il segnale INT

2. Il processore riceve INT e invia un impulso negativo di INTA al PIC

3. Sul primo impulso INTA, il PIC abbassa INT e scrive sul bus dati l’identificativo del dispositivo nei bit

meno significativi (D2-D0)

4. Il processore legge l’identificativo e si prepara a servire l’interrupt

5. Sul secondo impulso INTA, il PIC disabilita il bus dati e si aspetta che la periferica abbassi la sua linea INT,

quindi si riporta nello stato iniziale

Facciamo l’assunzione che la maschera sia gestita internamente dal PIC: in fase di simulazione, forzeremo la

maschera a particolari valori per testarne le funzionalità. Nel nostro caso, la maschera non è selettiva come nel

protocollo reale, ma maschera soltanto gli interrupt maggiori del valore indicato (o nessuno).

3. Progettazione VHDL di basso livello

Il PIC è stato implementato in VHDL mediante componenti di più basso livello, applicando il principio di granularità di

VHDL: mentre per alcuni dei componenti è stata utilizzata una descrizione comportamentale del funzionamento,

essenzialmente dovuta alla laboriosità della progettazione in logica a due livelli, per altri sono invece state

implementate le effettive architetture fino al livello gate.

Per quanto riguarda la logica, abbiamo ritenuto interessante utilizzare la logica NOR. La scelta delle gate

normalmente è dettata dalla tecnologia su silicio attualmente in uso. Supponendo di lavorare in NMOS, sarebbe più

economico realizzare gate NOR. Le specifiche sulle gate vengono imposte dal produttore dell’hardware e/o dal

committente e non dal progettista, il quale si deve attenere ad esse. Grazie a VHDL, come vedremo, sarà possibile in

maniera molto semplice lo switch da una tecnologia all’altra cambiando semplicemente la dichiarazione delle

architetture nell’istanziazione dei componenti.

Siamo dunque partiti dalla progettazione degli elementi di più basso livello, utili in seguito a implementare i

principali componenti del PIC: abbiamo cioè implementato le gate NOR a due ingressi, e da lì le porte AND, OR e NOT

utilizzando le suddette NOR. Qui va fatta una precisazione doverosa: implementare le tre gate fondamentali per

mezzo di un’altra gate è un passaggio decisamente ridondante, in quanto il progettista ha gli strumenti per

effettuare direttamente la progettazione in una data logica di una rete. Tuttavia, poiché in alcuni punti, come ad

esempio nella maschera di interrupt, la funzionalità di più alto livello da implementare è una AND bit a bit, anziché

implementare tale funzionalità direttamente in NOR, abbiamo preferito aggiungere un layer di astrazione utilizzando

le entità AND2, che a loro volta sono mappate su entità NOR2. Nel caso fosse necessario passare ad una logica

NAND, basterebbe cambiare le istanze delle suddette AND2 verso una implementazione basata su NAND.

Tale comportamento non giustifica, tuttavia, l’utilizzo di tutto il set di gate nella progettazione delle reti più

complesse: per quanto ciò crei la massima astrazione e riuso possibile, una progettazione generica non è in grado di

ottimizzare il circuito logico per l’implementazione, aggiungendo livelli logici e ritardi che si potrebbero evitare

effettuando progettazioni separate per NAND e NOR anziché un’unica progettazione riusabile. Il motivo per cui noi lo

abbiamo fatto è stato però quello di mostrare, a titolo illustrativo, le potenzialità del linguaggio. Ma proprio in virtù

del potenziale di VHDL, qualunque progettista che metta mano ai nostri sorgenti potrà aggiungere, molto

semplicemente, una implementazione basata su NOR2 e/o su NAND2 dei medesimi componenti rispettandone

l’interfaccia e ottenendo un circuito ottimizzato.

Ciò premesso, analizziamo il contenuto del file NOR2.VHD realizzato con PeakVHDL. Questa libreria, realizzata

appositamente per questo progetto, contiene essenzialmente le porte logiche comuni e un registro D a 8 bit

asincrono implementato mediante flip-flop RS a NOR incrociate.

ENTITÀ NOR2

Dato che questa gate è per noi atomica, la implementiamo come behavioural, lasciando al produttore del circuito la

mappatura su transistor. Di seguito il codice sorgente:

entity NOR2 is

generic ( port_delay: time := 5ns );

port (

-- WIZ BEGINPORTS (Entity Wizard command)

x: in std_logic;

y: in std_logic;

o: out std_logic

-- WIZ ENDPORTS (Entity Wizard command)

);

end NOR2;

architecture BEHAVIOR of NOR2 is

-- Note: signals, components and other objects may be declared here if needed.

begin o <= x nor y after port_delay;

end BEHAVIOR;

ENTITÀ INV

Questa è una semplice porta NOT, realizzata implementando una singola gate NOR2 che in ingresso prende la stessa

variabile in entrambe le porte. Di seguito il sorgente:

entity inv is port ( x: in std_logic;

y: out std_logic

);

end inv;

architecture withnor2 of inv is

component n port (x, y: in std_logic; o: out std_logic);

end component;

for all: n use entity work.nor2(behavior);


PAGINE

18

PESO

1.12 MB

PUBBLICATO

+1 anno fa


DETTAGLI
Corso di laurea: Corso di laurea in ingegneria informatica
SSD:
A.A.: 2013-2014

I contenuti di questa pagina costituiscono rielaborazioni personali del Publisher valeria0186 di informazioni apprese con la frequenza delle lezioni di Calcolatori Elettronici II e studio autonomo di eventuali libri di riferimento in preparazione dell'esame finale o della tesi. Non devono intendersi come materiale ufficiale dell'università Napoli Federico II - Unina o del prof Mazzeo Antonino.

Acquista con carta o conto PayPal

Scarica il file tutte le volte che vuoi

Paga con un conto PayPal per usufruire della garanzia Soddisfatto o rimborsato

Recensioni
Ti è piaciuto questo appunto? Valutalo!

Altri appunti di Calcolatori elettronici ii

Calcolatori Elettronici II – Gerarchia memorie
Dispensa
Calcolatori Elettronici II - Reti Sequenziali
Dispensa
Calcolatori Elettronici II
Dispensa
Calcolatori Elettronici II - Tesina
Appunto